<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.3.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/yuwanzi.io/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/yuwanzi.io/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/yuwanzi.io/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/yuwanzi.io/images/logo.svg" color="#222">

<link rel="stylesheet" href="/yuwanzi.io/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"suyuhuan.gitee.io","root":"/yuwanzi.io/","images":"/yuwanzi.io/images","scheme":"Muse","version":"8.2.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Suche...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}};
  </script>
<meta name="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
<meta property="og:type" content="website">
<meta property="og:title" content="玉丸子 | Blog">
<meta property="og:url" content="https://suyuhuan.gitee.io/yuwanzi.io/page/4/index.html">
<meta property="og:site_name" content="玉丸子 | Blog">
<meta property="og:description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
<meta property="og:locale">
<meta property="article:author" content="玉丸子">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://suyuhuan.gitee.io/yuwanzi.io/page/4/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh'
  };
</script>
<title>玉丸子 | Blog</title>
  




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<link rel="alternate" href="/yuwanzi.io/atom.xml" title="玉丸子 | Blog" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Navigationsleiste an/ausschalten" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/yuwanzi.io/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">玉丸子 | Blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Inhaltsverzeichnis
        </li>
        <li class="sidebar-nav-overview">
          Übersicht
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">玉丸子</p>
  <div class="site-description" itemprop="description">这里是玉丸子的个人博客,与你一起发现更大的世界。</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/yuwanzi.io/archives">
          <span class="site-state-item-count">68</span>
          <span class="site-state-item-name">Artikel</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/yuwanzi.io/categories/">
        <span class="site-state-item-count">39</span>
        <span class="site-state-item-name">Kategorien</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/yuwanzi.io/tags/">
        <span class="site-state-item-count">46</span>
        <span class="site-state-item-name">schlagwörter</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/06/09/2017-06-09-sort_algorithms_heapSort/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/06/09/2017-06-09-sort_algorithms_heapSort/" class="post-title-link" itemprop="url">深入浅出排序算法(1)-堆排序</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-06-09 18:00:00" itemprop="dateCreated datePublished" datetime="2017-06-09T18:00:00+08:00">2017-06-09</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">排序算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p><code>堆排序</code>即是利用<code>堆</code>这个数据结构来完成排序的.所以,要想理解<code>堆排序</code>就要先了解<code>堆</code>.</p>
<h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><hr>
<p><code>堆(Heap)</code>是一种数据结构,它可以被看做是一棵树的数组对象.一个<code>二叉堆</code>拥有以下性质.</p>
<ul>
<li>父节点<code>k</code>的左子节点在数组中的索引位置为<code>2 * k + 1</code>.</li>
</ul>
<ul>
<li>父节点<code>k</code>的右子节点在数组中的索引位置为<code>2 * k + 2</code>.</li>
</ul>
<ul>
<li>子节点<code>i</code>的父节点在数组中的索引位置为<code>(i - 1) / 2</code>.</li>
</ul>
<ul>
<li>父节点<code>k</code>的任意子节点都必须小于(或大于)<code>k</code>.</li>
</ul>
<ul>
<li>根节点必须是最大节点(或最小节点).</li>
</ul>
<h4 id="最大堆代码实现"><a href="#最大堆代码实现" class="headerlink" title="最大堆代码实现"></a>最大堆代码实现</h4><hr>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MaxHeap</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Comparable</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    T[] heap;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">MaxHeap</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MaxHeap</span><span class="params">(T[] heap)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.heap = heap;</span><br><span class="line">        buildHeap();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 自底向上构建堆</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">buildHeap</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> length = heap.length;</span><br><span class="line">        <span class="comment">// 当堆为空或者长度为1时不需要任何操作</span></span><br><span class="line">        <span class="keyword">if</span> (length &lt;= <span class="number">1</span>)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> root = (length - <span class="number">2</span>) &gt;&gt;&gt; <span class="number">1</span>; <span class="comment">// (i - 1) / 2</span></span><br><span class="line">        <span class="keyword">while</span> (root &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            heapify(heap, length, root);</span><br><span class="line">            root--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 调整堆的结构</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> heap   堆</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> length 堆的长度</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> root   根节点索引</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">heapify</span><span class="params">(T[] heap, <span class="keyword">int</span> length, <span class="keyword">int</span> root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (root &gt;= length)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> largest = root; <span class="comment">// 表示root,left,right中最大值的变量</span></span><br><span class="line">        <span class="keyword">int</span> left = (root &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>; <span class="comment">// 左子节点,root * 2 + 1</span></span><br><span class="line">        <span class="keyword">int</span> right = left + <span class="number">1</span>; <span class="comment">// 右子节点,root * 2 + 2</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 找出最大值</span></span><br><span class="line">        <span class="keyword">if</span> (left &lt; length &amp;&amp; greater(heap[left], heap[largest]))</span><br><span class="line">            largest = left;</span><br><span class="line">        <span class="keyword">if</span> (right &lt; length &amp;&amp; greater(heap[right], heap[largest]))</span><br><span class="line">            largest = right;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果largest发生变化,将largest与root交换</span></span><br><span class="line">        <span class="keyword">if</span> (largest != root) &#123;</span><br><span class="line">            T t = heap[root];</span><br><span class="line">            heap[root] = heap[largest];</span><br><span class="line">            heap[largest] = t;</span><br><span class="line">            <span class="comment">// 继续向下调整堆</span></span><br><span class="line">            heapify(heap, length, largest);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">greater</span><span class="params">(Comparable a, Comparable b)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a.compareTo(b) &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="优先队列"><a href="#优先队列" class="headerlink" title="优先队列"></a>优先队列</h3><hr>
<p>普通的队列是基于<code>先进先出</code>的,也就是说最先入队的元素永远是在第一位,而<code>优先队列</code>中的每一个元素都是拥有<code>优先级</code>的,<code>优先级</code>最高的元素永远在第一位.</p>
<p><code>优先队列</code>也是<code>贪心算法</code>的体现,所谓的<code>贪心算法</code>即是在问题求解的每一步中总是选择当前最好的结果.</p>
<p><code>堆</code>就是用于实现<code>优先队列</code>的,因为<code>堆</code>的性质与<code>优先队列</code>十分吻合.</p>
<h4 id="添加"><a href="#添加" class="headerlink" title="添加"></a>添加</h4><hr>
<p>往<code>优先队列</code>中添加元素时,我们只需要将元素添加到数组末尾并调整堆(以下例子均是以最大堆为例).</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (t == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">       <span class="keyword">if</span> (size == queue.length)</span><br><span class="line">           resize(queue.length * <span class="number">2</span>);</span><br><span class="line">       <span class="keyword">int</span> i = size;</span><br><span class="line">	<span class="comment">// 如果当前队列为空,则不需要进行堆调整直接插入元素即可</span></span><br><span class="line">       <span class="keyword">if</span> (i == <span class="number">0</span>)</span><br><span class="line">           queue[<span class="number">0</span>] = t;</span><br><span class="line">       <span class="keyword">else</span></span><br><span class="line">           swim(i, t);</span><br><span class="line">       size++;</span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上浮调整</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">swim</span><span class="params">(<span class="keyword">int</span> i, T t)</span> </span>&#123;</span><br><span class="line">       Comparable&lt;? <span class="keyword">super</span> T&gt; key = (Comparable) t;</span><br><span class="line">       <span class="keyword">while</span> (i &gt; <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="keyword">int</span> parent = (i - <span class="number">1</span>) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">           T p = (T) queue[parent];</span><br><span class="line">		<span class="comment">// 如果key小于他的父节点(符合最大堆规则)则结束调整</span></span><br><span class="line">           <span class="keyword">if</span> (key.compareTo(p) &lt; <span class="number">0</span>)</span><br><span class="line">               <span class="keyword">break</span>;</span><br><span class="line">           queue[i] = p;</span><br><span class="line">           i = parent;</span><br><span class="line">       &#125;</span><br><span class="line">       queue[i] = key;</span><br><span class="line">   &#125;	</span><br></pre></td></tr></table></figure>
<h4 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h4><hr>
<p>删除操作要稍微麻烦一点,将<code>优先队列</code>中末尾的元素放到队头并进行堆调整.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">   <span class="function"><span class="keyword">public</span> T <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (isEmpty())</span><br><span class="line">           <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">       <span class="keyword">int</span> s = --size;</span><br><span class="line">       Object result = queue[<span class="number">0</span>];</span><br><span class="line">       Object end = queue[s];</span><br><span class="line">       queue[s] = <span class="keyword">null</span>;</span><br><span class="line">       <span class="keyword">if</span> (s != <span class="number">0</span>)</span><br><span class="line">           sink(<span class="number">0</span>, (T) end);</span><br><span class="line">       <span class="keyword">if</span> (size &lt;= queue.length / <span class="number">4</span>)</span><br><span class="line">           resize(queue.length / <span class="number">2</span>);</span><br><span class="line">       <span class="keyword">return</span> (T) result;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下沉调整</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sink</span><span class="params">(<span class="keyword">int</span> i, T t)</span> </span>&#123;</span><br><span class="line">       Comparable&lt;? <span class="keyword">super</span> T&gt; key = (Comparable&lt;? <span class="keyword">super</span> T&gt;) t;</span><br><span class="line">       <span class="keyword">int</span> half = size &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">       <span class="keyword">while</span> (i &lt; half) &#123;</span><br><span class="line">           <span class="keyword">int</span> child = (i &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>; <span class="comment">// 左子节点</span></span><br><span class="line">           <span class="keyword">int</span> right = child + <span class="number">1</span>; <span class="comment">// 右子节点</span></span><br><span class="line">           T max = (T) queue[child];</span><br><span class="line">           <span class="comment">// find maximum element</span></span><br><span class="line">           <span class="keyword">if</span> (right &lt; size &amp;&amp;</span><br><span class="line">                   ((Comparable&lt;? <span class="keyword">super</span> T&gt;) max).compareTo((T) queue[right]) &lt; <span class="number">0</span>)</span><br><span class="line">               max = (T) queue[child = right];</span><br><span class="line">		<span class="comment">// key大于它的最大子节点(符合最大堆规则)则结束调整	</span></span><br><span class="line">           <span class="keyword">if</span> (key.compareTo(max) &gt; <span class="number">0</span>)</span><br><span class="line">               <span class="keyword">break</span>;</span><br><span class="line">           queue[i] = max;</span><br><span class="line">           i = child;</span><br><span class="line">       &#125;</span><br><span class="line">       queue[i] = key;</span><br><span class="line">   &#125;	</span><br></pre></td></tr></table></figure>
<p><a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/test-demo/blob/master/src/main/java/com/sun/sylvanas/data_struct/heap/MaxPriorityQueue.java">点击查看优先队列完整代码</a></p>
<h3 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h3><hr>
<p>实现<code>堆排序</code>有两种方法,一种是使用<code>优先队列</code>,另一种是直接使用<code>堆</code>.</p>
<h4 id="直接使用堆实现堆排序"><a href="#直接使用堆实现堆排序" class="headerlink" title="直接使用堆实现堆排序"></a>直接使用堆实现堆排序</h4><hr>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用最大堆实现堆排序</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">maxHeapSort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">    MaxHeap&lt;Comparable&gt; maxHeap = <span class="keyword">new</span> MaxHeap&lt;&gt;(a);</span><br><span class="line">    <span class="comment">//不断地将最大堆中顶端元素(最大值)与最底部的元素(最小值)交换</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = a.length - <span class="number">1</span>; i &gt; <span class="number">0</span>; i--) &#123;</span><br><span class="line">        Comparable largest = a[<span class="number">0</span>];</span><br><span class="line">        a[<span class="number">0</span>] = a[i];</span><br><span class="line">        a[i] = largest;</span><br><span class="line">        <span class="comment">// 堆减少,并调整新的堆</span></span><br><span class="line">        maxHeap.heapify(a, i, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="使用优先队列实现堆排序"><a href="#使用优先队列实现堆排序" class="headerlink" title="使用优先队列实现堆排序"></a>使用优先队列实现堆排序</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用优先队列实现堆排序</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">pqSort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">    MinPriorityQueue&lt;Comparable&gt; priorityQueue = <span class="keyword">new</span> MinPriorityQueue&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length; i++) &#123;</span><br><span class="line">        priorityQueue.add(a[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length; i++) &#123;</span><br><span class="line">        a[i] = priorityQueue.poll();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>本文作者为<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun(sylvanassun_xtz@163.com)</a>,转载请务必指明原文链接.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/06/07/2017-06-07-IoC&AOP/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/06/07/2017-06-07-IoC&AOP/" class="post-title-link" itemprop="url">IoC与AOP的那点事儿</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-06-07 16:00:00" itemprop="dateCreated datePublished" datetime="2017-06-07T16:00:00+08:00">2017-06-07</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring/" itemprop="url" rel="index"><span itemprop="name">Spring</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="IoC"><a href="#IoC" class="headerlink" title="IoC"></a>IoC</h3><hr>
<p>控制反转(Inversion of Control)是<code>OOP</code>中的一种设计原则,也是<code>Spring</code>框架的核心.大多数应用程序的业务逻辑代码都需要两个或多个类进行合作完成的,<strong>通过<code>IoC</code>则可以减少它们之间的耦合度</strong>.</p>
<h4 id="实现方法"><a href="#实现方法" class="headerlink" title="实现方法"></a>实现方法</h4><hr>
<p><code>IoC</code>的主要实现方法有两种,<code>依赖注入</code>与<code>依赖查找</code>.</p>
<p> <strong>依赖注入 :</strong>  应用程序被动的接收对象,<code>IoC</code>容器通过类型或名称等信息来判断将不同的对象注入到不同的属性中.</p>
<p>依赖注入主要有以下的方式: </p>
<ul>
<li> 基于<code>set</code>方法 : 实现特定属性的public set()方法,来让<code>IoC</code>容器调用注入所依赖类型的对象.</li>
</ul>
<ul>
<li> 基于接口 : 实现特定接口以供<code>IoC</code>容器注入所依赖类型的对象.</li>
</ul>
<ul>
<li><p> 基于构造函数 : 实现特定参数的构造函数,在创建对象时来让<code>IoC</code>容器注入所依赖类型的对象.</p>
</li>
<li><p> 基于注解 : 通过<code>Java</code>的注解机制来让<code>IoC</code>容器注入所依赖类型的对象,例如<code>Spring</code>框架中的<code>@Autowired</code>.</p>
</li>
</ul>
<p><strong>依赖查找 :</strong> 它相对于<code>依赖注入</code>而言是一种更为主动的方法,它会在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态.</p>
<h4 id="IoC的思想"><a href="#IoC的思想" class="headerlink" title="IoC的思想"></a>IoC的思想</h4><hr>
<p>在传统实现中,我们都是通过应用程序自己来管理依赖的创建,例如下代码.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 由Person自己管理Food类的创建</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		Food food = <span class="keyword">new</span> Chicken();</span><br><span class="line">		System.out.println(<span class="string">&quot;I am eating &quot;</span> + food.getName() + <span class="string">&quot;...&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>而<code>IoC</code>则是通过一个<strong>第三方容器来管理并维护这些被依赖对象,应用程序只需要接收并使用<code>IoC容器</code>注入的对象而不需要关注其他事情</strong>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">	<span class="keyword">private</span> Food food;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 通过set注入</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setFood</span><span class="params">(Food food)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">this</span>.food = food;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// Person不需要关注Food,只管使用即可</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		System.out.println(<span class="string">&quot;I am eating &quot;</span> + <span class="keyword">this</span>.food.getName() + <span class="string">&quot;...&quot;</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过以上的例子我们能够发现,控制反转其实就是对象控制权的转移,<strong>应用程序将对象的控制权转移给了第三方容器并通过它来管理这些被依赖对象,完成了应用程序与被依赖对象的解耦.</strong></p>
<h3 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h3><hr>
<p><code>AOP(Aspect-Oriented Programming)</code>即面向方面编程.<strong>它是一种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想.**用于</strong>切入到指定类指定方法的代码片段叫做<code>切面</code><strong>,而</strong>切入到哪些类中的哪些方法叫做<code>切入点</code>.**</p>
<p><code>AOP</code>是<code>OOP</code>的有益补充,<code>OOP</code>从横向上区分出了一个个类,<code>AOP</code>则从纵向上向指定类的指定方法中动态地切入代码.它使<code>OOP</code>变得更加立体.</p>
<p><code>Java</code>中的动态代理或<code>CGLib</code>就是<code>AOP</code>的体现.</p>
<h4 id="案例分析"><a href="#案例分析" class="headerlink" title="案例分析"></a>案例分析</h4><hr>
<p>在<code>OOP</code>中,我们使用封装的特性来将不同职责的代码抽象到不同的类中.但是在分散代码的同时,也增加了代码的重复性.</p>
<p>例如,我们需要在两个或多个类中的方法都记录日志或执行时间,可能这些代码是完全一致的,但因为类与类无法联系造成代码重复.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">something</span> <span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 业务逻辑...</span></span><br><span class="line">		recordLog();	</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">recordLog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 记录日志...</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">something</span> <span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 业务逻辑...</span></span><br><span class="line">		recordLog();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">recordLog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 记录日志...</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>接下来,我们采取两种不同方案来改进这段代码.</p>
<h4 id="将重复代码抽离到一个类中"><a href="#将重复代码抽离到一个类中" class="headerlink" title="将重复代码抽离到一个类中"></a>将重复代码抽离到一个类中</h4><hr>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">something</span> <span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 业务逻辑...</span></span><br><span class="line">		Report.recordLog();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">something</span> <span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 业务逻辑...</span></span><br><span class="line">		Report.recordLog();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Report</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">recordLog</span> <span class="params">(String ...messages)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 记录日志...</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样看似解决了问题,但类之间已经耦合了.并且当这些外围业务代码(日志,权限校验等)越来越多时,它们的侵入(与核心业务代码混在一起)会使代码的整洁度变得混乱不堪.</p>
<h4 id="使用AOP分离外围业务代码"><a href="#使用AOP分离外围业务代码" class="headerlink" title="使用AOP分离外围业务代码"></a>使用AOP分离外围业务代码</h4><hr>
<p>我们使用<code>AspectJ</code>,它是一个<code>AOP</code>框架,扩展了Java语言,并定义了<code>AOP</code>语法(通过它实现的编译器).</p>
<p>使用<code>AspectJ</code>需要先安装并将lib中aspectjrt.jar添加进入classpath,<a target="_blank" rel="noopener" href="http://www.eclipse.org/aspectj/downloads.php">下载地址</a>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Something</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">say</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Say something...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Something something = <span class="keyword">new</span> Something();</span><br><span class="line">        something.say();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> aspect SomethingAspect &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 切入点,切入到Something.say()</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">pointcut <span class="title">recordLog</span><span class="params">()</span>:<span class="title">call</span><span class="params">(* com.sun.sylvanas.application.hello_aop.Something.say(..)</span>)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在方法执行后执行</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    after():recordLog() &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;[AFTER] Record log...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>AOP</code>解决了代码的重复并将这些外围业务代码抽离到一个切面中,我们可以动态地将切面切入到切入点.</p>
<blockquote>
<p>本文作者为<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun(sylvanassun_xtz@163.com)</a>,转载请务必指明原文链接.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/05/29/2017-5-29-cas_concurrent_stack/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/05/29/2017-5-29-cas_concurrent_stack/" class="post-title-link" itemprop="url">谈谈如何实现一个非阻塞的线程安全的集合</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-05-29 18:00:00" itemprop="dateCreated datePublished" datetime="2017-05-29T18:00:00+08:00">2017-05-29</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" itemprop="url" rel="index"><span itemprop="name">多线程</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/%E5%A4%9A%E7%BA%BF%E7%A8%8B/CAS/" itemprop="url" rel="index"><span itemprop="name">CAS</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>众所周知,想要在<code>java</code>中要实现一个线程安全的类有很多方法.最简单直接的即是使用<code>synchronized</code>关键字或<code>ReentrantLock</code>.</p>
<p>但是,这两种同步方法都是基于锁的,基于锁的同步方法是阻塞的,即未争夺到锁的线程需要阻塞等待(或挂起)直到锁可用.</p>
<p>这种方法具有一些明显的缺点:</p>
<ul>
<li>被阻塞的线程无法去做任何其他事情,如果这个线程是优先级较高的线程甚至会发生非常不好的结果(优先级倒置).</li>
</ul>
<ul>
<li>由于<code>java</code>的线程模型是基于内核线程实现的,挂起恢复线程需要来回地切换到内核态,性能开销很大.</li>
</ul>
<ul>
<li>当两个(或多个)线程都阻塞着等待另一方释放锁时,将会引发死锁.</li>
</ul>
<p>那么有非阻塞的方法来实现同步吗?(<code>volatile</code>关键字也是非阻塞的,但它只保证了数据的可见性与有序性,并不保证原子性)</p>
<p>有!在<code>jdk5</code>中,java增加了大量的原子类来保证无锁下的操作原子性,可以说<code>java.util.concurrent</code>包下的所有类都几乎用到了这些原子类.</p>
<h3 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h3><hr>
<p>这些原子类都是基于<code>CAS</code>实现的,<code>CAS</code>即是<strong>Compare And Swap</strong>,它的原理简单来讲就是<strong>在更新新值之前先去比较原值有没有发生变化,如果没发生变化则进行更新</strong>.</p>
<p><code>java</code>中的<code>CAS</code>是通过<code>Unsafe</code>类中的本地方法实现的,而这些本地方法需要通过现代处理器提供的<code>CAS</code>指令实现(在<code>Intel</code>处理器中该指令为<code>cmpxchg</code>).</p>
<p>所以我们发现,<strong><code>CAS</code>操作的原子性是由处理器来保证的</strong>.</p>
<h4 id="比较的过程"><a href="#比较的过程" class="headerlink" title="比较的过程"></a>比较的过程</h4><p>在<code>CAS</code>操作中包含了三个数,<code>V(内存位置)</code>,<code>A(预期值)</code>,<code>B(新值)</code>.</p>
<ul>
<li>首先会将<code>V</code>与<code>A</code>进行匹配.</li>
</ul>
<ul>
<li>如果两个值相等,则使用<code>B</code>作为新值进行更新.</li>
</ul>
<ul>
<li>如果不相等,则不进行更新操作(一般的补救措施是继续进行请求).</li>
</ul>
<h4 id="与锁相比的优点"><a href="#与锁相比的优点" class="headerlink" title="与锁相比的优点"></a>与锁相比的优点</h4><ul>
<li><p><code>CAS</code>操作是无锁的实现,所以它不会发生死锁情况.</p>
</li>
<li><p>虽然<code>CAS</code>操作失败需要不断的进行请求重试,但相对于不断地挂起或恢复线程来说,性能开销要低得多.</p>
</li>
<li><p><code>CAS</code>的粒度更细,操作也更加轻量与灵活.</p>
</li>
</ul>
<h3 id="ConcurrentStack"><a href="#ConcurrentStack" class="headerlink" title="ConcurrentStack"></a>ConcurrentStack</h3><hr>
<p>我们通过实现一个简单的<code>ConcurentStack</code>来看看<code>CAS</code>操作是如何保证线程安全的.</p>
<p><a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/15353e5567e1890b45f516f7fe6a187d">完整代码请从作者的Gist中获取</a></p>
<h4 id="节点的实现"><a href="#节点的实现" class="headerlink" title="节点的实现"></a>节点的实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcurrentStack</span>&lt;<span class="title">E</span>&gt; <span class="keyword">implements</span> <span class="title">Iterable</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> AtomicReference&lt;Node&lt;E&gt;&gt; head = <span class="keyword">new</span> AtomicReference&lt;&gt;(<span class="keyword">null</span>);</span><br><span class="line">    <span class="keyword">private</span> AtomicInteger size = <span class="keyword">new</span> AtomicInteger(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This internal static class represents the nodes in the stack.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> E value;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">Node</span><span class="params">(E value, Node&lt;E&gt; next)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">            <span class="keyword">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="push"><a href="#push" class="headerlink" title="push"></a>push</h4><p>push函数主要是通过观察头节点(这里的头节点即是<code>V</code>),然后构建一个新的节点(它代表<code>B</code>)放于栈顶,如果<code>V</code>没有发生变化,则进行更新.如果发生了变化(被其他线程修改),就重新尝试进行<code>CAS</code>操作.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Insert a new element to the this stack.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> if &#123;<span class="doctag">@code</span> true&#125; insert success,&#123;<span class="doctag">@code</span> false&#125; otherwise</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IllegalArgumentException if &#123;<span class="doctag">@code</span> value&#125; is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">put</span><span class="params">(E value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="keyword">return</span> putAndReturnResult(value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">putAndReturnResult</span><span class="params">(E value)</span> </span>&#123;</span><br><span class="line">    Node&lt;E&gt; oldNode;</span><br><span class="line">    Node&lt;E&gt; newNode;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        oldNode = head.get();</span><br><span class="line">        newNode = <span class="keyword">new</span> Node&lt;E&gt;(value, oldNode);</span><br><span class="line">    &#125; <span class="keyword">while</span> (!head.compareAndSet(oldNode, newNode));</span><br><span class="line">    sizePlusOne();</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="pop"><a href="#pop" class="headerlink" title="pop"></a>pop</h4><p>pop函数中的<code>CAS</code>操作的思想基本与push函数一致.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Return the element of stack top and remove this element.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if this stack is empty</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (isEmpty())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="keyword">return</span> removeAndReturnElement();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> E <span class="title">removeAndReturnElement</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Node&lt;E&gt; oldNode;</span><br><span class="line">    Node&lt;E&gt; newNode;</span><br><span class="line">    E result;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        oldNode = head.get();</span><br><span class="line">        newNode = oldNode.next;</span><br><span class="line">        result = oldNode.value;</span><br><span class="line">    &#125; <span class="keyword">while</span> (!head.compareAndSet(oldNode, newNode));</span><br><span class="line">    sizeMinusOne();</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<p>非阻塞的算法实现的复杂度要比阻塞算法复杂的多,但它能带来更少的性能开销,在<code>jdk</code>中,很多线程安全类都是在尽量地避免使用锁的基础上来实现线程安全.</p>
<blockquote>
<p>本文作者为<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun(sylvanassun_xtz@163.com)</a>,转载请务必指明原文链接.</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/04/13/2017-4-13-hash_table/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/04/13/2017-4-13-hash_table/" class="post-title-link" itemprop="url">《Algorithms,4th Edition》读书笔记-散列表</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-04-13 18:00:00" itemprop="dateCreated datePublished" datetime="2017-04-13T18:00:00+08:00">2017-04-13</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/HashTable/" itemprop="url" rel="index"><span itemprop="name">HashTable</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p><code>散列表</code>(<code>Hash Table</code>,也叫<code>哈希表</code>),它是根据键而直接访问在内存存储位置的数据结构.也可以说是用一个数组来实现的<strong>无序的符号表</strong>,将键作为数组的索引而数组中键<code>i</code>处存储的就是它对应的值.</p>
<p><code>散列表</code>通过<code>散列函数</code>将键转化为数组的索引来访问数组中的键值对.</p>
<p>在<code>散列表</code>的算法中,最重要的两个操作如下.</p>
<ol>
<li>使用<code>散列函数</code>将被查找的键转化为数组的一个索引.</li>
</ol>
<ol start="2">
<li>处理散列表中的<code>碰撞冲突</code>问题.</li>
</ol>
<p><img src="http://algs4.cs.princeton.edu/34hash/images/hashing-crux.png"></p>
<h3 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h3><hr>
<ul>
<li>若关键字为<code>k</code>,则其值存放于<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/c36f16f5357aeb5b0fa2fe3040e74282d62f8881">的存储位置上.由此,不需要比较便可直接取得所查记录.称这个对应关系<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/132e57acb643253e7810ee9702d9581f159a1c61">为<code>散列函数</code>,按照这个思想建立的符合表为<code>散列表</code>.</li>
</ul>
<ul>
<li>对不同的键可能会得到同一个<code>散列地址</code>,即<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/f2b910a452063a4769272110d8d22cab053d433d">,而<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/fa1d43b27a17bf57baf12626ad7cfbf8ee9bb96d">,这种现象被称为<code>碰撞冲突</code>.具有相同函数值的键对该<code>散列函数</code>来说称做同义词.综上所述,根据散列函数<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/c36f16f5357aeb5b0fa2fe3040e74282d62f8881">和处理<code>碰撞冲突</code>的方法将一组键映射到一个有限的连续的地址集(区间)上,这种表称为<code>散列表</code>,这一映射过程称为<code>散列</code>,所得的存储位置称为<code>散列地址</code>.</li>
</ul>
<ul>
<li>若对于键集合中的任一个键,经<code>散列函数</code>映射到地址集合中任何一个地址的概率是相等的,则这个<code>散列函数</code>被称为<code>均匀散列函数</code>,它可以减少<code>碰撞冲突</code>.</li>
</ul>
<h3 id="散列函数"><a href="#散列函数" class="headerlink" title="散列函数"></a>散列函数</h3><hr>
<p><code>散列函数</code>用于将键转化为数组的索引.如果我们有一个能够保存M个键值对的数组,那么我们就需要一个能够将任意键转化为该数组范围内的索引([0,M-1]范围内的整数)的<code>散列函数</code></p>
<p><code>散列函数</code>与键的类型有关,对于每种类型的键都需要一个与之对应的<code>散列函数</code>.</p>
<h4 id="实现散列函数的几种方法"><a href="#实现散列函数的几种方法" class="headerlink" title="实现散列函数的几种方法"></a>实现散列函数的几种方法</h4><ul>
<li>直接定址法 : 取<code>key</code>或者<code>key</code>的某个线性函数值为<code>散列地址</code>.即<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/989ebc7db55ece5d29e2a8baa005e876ef486e4e">或<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/989ebc7db55ece5d29e2a8baa005e876ef486e4e">其中a,b为常数(这种<code>散列函数</code>叫做自身函数).</li>
</ul>
<ul>
<li>数字分析法 : 假设<code>key</code>是以<code>r</code>为基的数,并且<code>散列表</code>中可能出现的<code>key</code>都是事先知道的,则可取<code>key</code>的若干数位组成<code>散列地址</code>.</li>
</ul>
<ul>
<li>平方取中法 : 取<code>key</code>平方后的中间几位为<code>散列地址</code>.通常在选定<code>散列函数</code>时不一定能知道<code>key</code>的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的<code>key</code>得到的<code>散列地址</code>也是随机的.取的位数由表长决定.</li>
</ul>
<ul>
<li>折叠法 : 将<code>key</code>分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为<code>散列地址</code>.</li>
</ul>
<ul>
<li>除留余数法 : 取<code>key</code>被某个不大于<code>散列表</code>长度<code>m</code>的数<code>p</code>除后所得的余数为<code>散列地址</code>.即<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/bc04a0c2f72156976761fa24dd4ba098855b7dca">,<img src="https://wikimedia.org/api/rest_v1/media/math/render/svg/3aad2b022083cbc8aef0745526f3a448e7d96160">.不仅可以对<code>key</code>直接取模，也可在<code>折叠法</code>、<code>平方取中法</code>等运算之后取模。对<code>p</code>的选择很重要，一般取<code>素数</code>或<code>m</code>，若<code>p</code>选择不好，容易产生<code>碰撞冲突</code>.</li>
</ul>
<h4 id="正整数"><a href="#正整数" class="headerlink" title="正整数"></a>正整数</h4><p>将正整数<code>散列</code>一般使用的是<code>除留余数法</code>.我们选择大小为<strong>素数</strong><code>M</code>的数组,对于任意正整数<code>k</code>,计算<code>k</code>除以<code>M</code>的余数(即<code>k%M</code>).它能够有效地将<code>key</code>散布在0到M-1的范围内.</p>
<p>如果<code>M</code>不是<strong>素数</strong>,可能无法利用<code>key</code>中包含的所有信息,这<strong>可能导致无法均匀地散列散列值</strong>.</p>
<p><img src="http://algs4.cs.princeton.edu/34hash/images/modular-hashing.png"></p>
<h4 id="浮点数"><a href="#浮点数" class="headerlink" title="浮点数"></a>浮点数</h4><p>对浮点数进行散列一般是将<code>key</code>表示为二进制数然后再使用<code>除留余数法</code>.</p>
<h4 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h4><p><code>除留余数法</code>也可以处理较长的<code>key</code>,例如字符串,我们只需将它们当成大整数即可.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> hash = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; s.length(); i++) &#123;</span><br><span class="line">   	hash = (R * hash + s.charAt(i)) % M;</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<p>Java的<code>charAt()</code>函数能够返回一个char值,即一个非负16位整数.如果<code>R</code>比任何字符的值都大,这种计算相当于将字符串当作一个N位的<code>R</code>进制值,将它除以<code>M</code>并取余.只要<code>R</code>足够小,不造成溢出,那么结果就能够落在0至M-1之间.可以使用一个较小的素数,例如31.</p>
<h4 id="组合键"><a href="#组合键" class="headerlink" title="组合键"></a>组合键</h4><p>如果<code>key</code>的类型含有多个整型变量,我们可以和字符串类型一样将它们混合起来.</p>
<p>例如,<code>key</code>的类型为Date,其中含有几个整型的域 : day(两个数字表示的日),month(两个数字表示的月),year(四个数字表示的年).我们可以这样计算它的散列值: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> hash = (((day * R + month) % M) * R + year) % M; </span><br></pre></td></tr></table></figure>
<h4 id="Java中的约定"><a href="#Java中的约定" class="headerlink" title="Java中的约定"></a>Java中的约定</h4><p>在Java中如果要为自定义的数据类型定义散列函数,需要同时重写<code>hashCode()</code>和<code>equals()</code>两个函数,并要遵守以下规则.</p>
<ul>
<li><code>hashCode()</code>与<code>equals()</code>的结果必须保持一致性.即<code>a.equals(b)</code>返回true,则<code>a.hashCode()</code>的返回值也必然和<code>b.hashCode()</code>的返回值相同.</li>
</ul>
<ul>
<li>但如果两个对象的<code>hashCode()</code>函数的返回值相同,这两个对象也有可能不同,还需要用<code>equals()</code>函数进行判断.</li>
</ul>
<p>一个使用<code>除留余数法</code>的简单<code>散列函数</code>如下,它会将符号位屏蔽(将一个32位整数变为一个31位非负整数).</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(Key key)</span> </span>&#123;</span><br><span class="line">   <span class="keyword">return</span> (key.hashCode() &amp; <span class="number">0x7fffffff</span>) % M;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="软缓存"><a href="#软缓存" class="headerlink" title="软缓存"></a>软缓存</h4><p>由于<code>散列函数</code>的计算有可能会很耗时,我们可以进行缓存优化,将每个<code>key</code>的散列值缓存起来(可以在每个<code>key</code>中使用一个hash变量来保存它的<code>hashCode()</code>的返回值).</p>
<p>当第一次调用<code>hashCode()</code>时,需要计算对象的散列值,但之后对<code>hashCode()</code>方法的调用会直接返回hash变量的值.</p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>总之,要想实现一个优秀的<code>散列函数</code>需要满足以下的条件.</p>
<ol>
<li>一致性,等价的<code>key</code>必然产生相等的散列值.</li>
</ol>
<ol start="2">
<li>.高效性,计算简便.</li>
</ol>
<ol start="3">
<li>均匀性,均匀地散列所有的<code>key</code>.</li>
</ol>
<h3 id="基于拉链法的散列表"><a href="#基于拉链法的散列表" class="headerlink" title="基于拉链法的散列表"></a>基于拉链法的散列表</h3><hr>
<p>拉链法是解决<code>碰撞冲突</code>的一种策略,它的核心思想是 : 将大小为<code>M</code>的<strong>数组中的每个元素指向一条链表</strong>,链表中的每个节点都存储了散列值为该元素的索引的键值对.</p>
<p>拉链法的实现一般分为以下两种: </p>
<ol>
<li>使用一个原始的链表数据类型来表示数组中的每个元素.</li>
</ol>
<ol start="2">
<li>使用一个符号表实现来表示数组中的每个元素(这个方法实现简单但效率偏低).</li>
</ol>
<p><img src="http://algs4.cs.princeton.edu/34hash/images/separate-chaining.png"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SeparateChainingHashST</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INIT_CAPACITY = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> n; <span class="comment">// the number of key-value pairs in the symbol table</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> m; <span class="comment">// the number of size of separate chaining table</span></span><br><span class="line">    <span class="keyword">private</span> Node&lt;K, V&gt;[] table; <span class="comment">// array of linked-list symbol tables</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> K key;</span><br><span class="line">        <span class="keyword">private</span> V value;</span><br><span class="line">        <span class="keyword">private</span> Node&lt;K,V&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(K key, V value, Node next)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">            <span class="keyword">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ((key.hashCode()) &amp; <span class="number">0x7fffffff</span>) % m;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<h4 id="查找、插入、删除"><a href="#查找、插入、删除" class="headerlink" title="查找、插入、删除"></a>查找、插入、删除</h4><p>基于拉链法的<code>散列表</code>的查找、插入、删除算法基本分为两步:</p>
<ol>
<li>首先根据散列值找到对应的链表.</li>
</ol>
<ol start="2">
<li>然后沿着这条链表进行相应的操作.</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called get() with key is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">int</span> i = hash(key);</span><br><span class="line">    Node x = table[i];</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (key.equals(x.key))</span><br><span class="line">            <span class="keyword">return</span> (V) x.value;</span><br><span class="line">        x = x.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">	</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called put() with key is null.&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>) &#123;</span><br><span class="line">        remove(key);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// double table size if average length of list &gt;= 10</span></span><br><span class="line">    <span class="keyword">if</span> (n &gt;= <span class="number">10</span> * m)</span><br><span class="line">        resize(<span class="number">2</span> * m);</span><br><span class="line">    <span class="keyword">int</span> i = hash(key);</span><br><span class="line">    Node x = table[i];</span><br><span class="line">    Node p = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (key.equals(x.key)) &#123;</span><br><span class="line">            x.value = value;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        p = x;</span><br><span class="line">        x = x.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (p == <span class="keyword">null</span>) &#123;</span><br><span class="line">        table[i] = <span class="keyword">new</span> Node(key, value, <span class="keyword">null</span>);</span><br><span class="line">        n++;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        p.next = <span class="keyword">new</span> Node(key, value, <span class="keyword">null</span>);</span><br><span class="line">        n++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">	</span><br><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">remove</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called remove() with key is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (isEmpty())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called remove() with empty symbol table.&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!contains(key))</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> i = hash(key);</span><br><span class="line">    Node x = table[i];</span><br><span class="line">    Node p = <span class="keyword">null</span>;</span><br><span class="line">    V oldValue = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (key.equals(x.key)) &#123;</span><br><span class="line">            oldValue = (V) x.value;</span><br><span class="line">            <span class="keyword">if</span> (p == <span class="keyword">null</span>) &#123;</span><br><span class="line">                table[i] = x.next;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                p.next = x.next;</span><br><span class="line">            &#125;</span><br><span class="line">            n--;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        p = x;</span><br><span class="line">        x = x.next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// halve table size if average length of list &lt;= 2</span></span><br><span class="line">    <span class="keyword">if</span> (m &gt; INIT_CAPACITY &amp;&amp; n &lt;= <span class="number">2</span> * m)</span><br><span class="line">        resize(m / <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<h3 id="基于线性探测法的散列表"><a href="#基于线性探测法的散列表" class="headerlink" title="基于线性探测法的散列表"></a>基于线性探测法的散列表</h3><hr>
<p>解决<code>碰撞冲突</code>的另一种策略是使用线性探测法.它的核心思想是: 使用大小为<code>M</code>的数组保存<code>N</code>个键值对,其中<code>M&gt;N</code>.这种方法<strong>需要依靠数组中的空位来解决<code>碰撞冲突</code></strong>,基于这种策略的所有方法被统称为<code>开放地址散列表</code>.</p>
<p><code>开放地址散列表</code>中最简单的方法就是线性探测法: 当发生<code>碰撞冲突</code>时,我们直接检查<code>散列表</code>中的下一个位置(将索引值加1).它可能会产生三种结果: </p>
<ol>
<li>命中,该位置的<code>key</code>和被查找的<code>key</code>相同.</li>
</ol>
<ol start="2">
<li>未命中,<code>key</code>为空(该位置没有<code>key</code>).</li>
</ol>
<ol start="3">
<li>继续查找,该位置的<code>key</code>和被查找的<code>key</code>不同.</li>
</ol>
<p>我们使用<code>散列函数</code>找到<code>key</code>在数组中的索引,检查其中的<code>key</code>和被查找的<code>key</code>是否相同.如果不同则继续查找(将索引值加1,到达数组结尾时折回数组的开头),直到找到该<code>key</code>或者遇到一个空元素.</p>
<p><code>开放地址散列表</code>的核心思想是: 与其将内存用作链表,不如将它们作为在<code>散列表</code>的空元素(这些空元素可以作为查找结束的标识).</p>
<p><img src="http://algs4.cs.princeton.edu/34hash/images/linear-probing.png"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LinearProbingHashST</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INIT_CAPACITY = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> n; <span class="comment">// the number of key-value pairs in the symbol table</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> m; <span class="comment">// the number of size of linear probing table</span></span><br><span class="line">    <span class="keyword">private</span> K[] keys; <span class="comment">// the keys</span></span><br><span class="line">    <span class="keyword">private</span> V[] vals; <span class="comment">// the values</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Initializes an empty symbol table.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LinearProbingHashST</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>(INIT_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Initializes an empty symbol table with the specified initial capacity.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> capacity the initial capacity</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LinearProbingHashST</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>&#123;</span><br><span class="line">        m = capacity;</span><br><span class="line">        n = <span class="number">0</span>;</span><br><span class="line">        keys = (K[]) <span class="keyword">new</span> Object[m];</span><br><span class="line">        vals = (V[]) <span class="keyword">new</span> Object[m];</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called get() with key is null.&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = hash(key); keys[i] != <span class="keyword">null</span>; i = (i + <span class="number">1</span>) % m) &#123;</span><br><span class="line">            <span class="keyword">if</span> (keys[i].equals(key))</span><br><span class="line">                <span class="keyword">return</span> vals[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called put() with key is null.&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (value == <span class="keyword">null</span>) &#123;</span><br><span class="line">            delete(key);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// double table size if 50% full</span></span><br><span class="line">        <span class="keyword">if</span> (n &gt;= m / <span class="number">2</span>) resize(<span class="number">2</span> * m);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> i;</span><br><span class="line">        <span class="keyword">for</span> (i = hash(key); keys[i] != <span class="keyword">null</span>; i = (i + <span class="number">1</span>) % m) &#123;</span><br><span class="line">            <span class="keyword">if</span> (keys[i].equals(key)) &#123;</span><br><span class="line">                vals[i] = value;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        keys[i] = key;</span><br><span class="line">        vals[i] = value;</span><br><span class="line">        n++;</span><br><span class="line">    &#125;	</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<h4 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h4><p>基于线性探测法的<code>散列表</code>的删除操作较为复杂,我们不能直接将<code>key</code>所在的位置设为<code>null</code>,这样会使在此位置之后的元素无法被查找到.</p>
<p>因此,我们需要<strong>将被删除键的右侧的所有键重新插入到<code>散列表</code>中</strong>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">delete</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called delete() with key is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (isEmpty())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called delete() with empty symbol table.&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!contains(key))</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// find position i of key</span></span><br><span class="line">    <span class="keyword">int</span> i = hash(key);</span><br><span class="line">    <span class="keyword">while</span> (!key.equals(keys[i])) &#123;</span><br><span class="line">        i = (i + <span class="number">1</span>) % m;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    V oldValue = vals[i];</span><br><span class="line">    <span class="comment">// delete key and associated value</span></span><br><span class="line">    keys[i] = <span class="keyword">null</span>;</span><br><span class="line">    vals[i] = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// rehash all keys in same cluster</span></span><br><span class="line">    i = (i + <span class="number">1</span>) % m;</span><br><span class="line">    <span class="keyword">while</span> (keys[i] != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// delete keys[i] an vals[i] and reinsert</span></span><br><span class="line">        K keyToRehash = keys[i];</span><br><span class="line">        V valToRehash = vals[i];</span><br><span class="line">        keys[i] = <span class="keyword">null</span>;</span><br><span class="line">        vals[i] = <span class="keyword">null</span>;</span><br><span class="line">        n--;</span><br><span class="line">        put(keyToRehash, valToRehash);</span><br><span class="line">        i = (i + <span class="number">1</span>) % m;</span><br><span class="line">    &#125;</span><br><span class="line">    n--;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// halves size of array if it&#x27;s 12.5% full or less</span></span><br><span class="line">    <span class="keyword">if</span> (n &gt; <span class="number">0</span> &amp;&amp; n &lt;= m / <span class="number">8</span>) resize(m / <span class="number">2</span>);</span><br><span class="line">    <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="键簇"><a href="#键簇" class="headerlink" title="键簇"></a>键簇</h4><p>线性探测法的平均成本取决于元素在插入数组后聚集成的一组连续的条目,也叫作<code>键簇</code>.</p>
<p>显然,短小的<code>键簇</code>才能保证较高的效率.随着插入的<code>key</code>越来越多,这个要求会很难满足,较长的<code>键簇</code>会越来越多.<code>长键簇</code>的可能性要比<code>短键簇</code>更大,因为新键的散列值无论落在<code>键簇</code>的任何位置都会使它的长度加1.</p>
<h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><hr>
<p><code>散列表</code>使用了适度的空间和时间并在这两个极端之间找到了一种平衡,所以它可以在一般应用中实现拥有(均摊后)常数级别的查找和插入操作的<code>符号表</code>.</p>
<p>但<code>散列表</code>是很难实现有序操作的,这是因为散列最主要的目的在于均匀地将键散布开来,因此在计算散列后键的顺序信息就已经丢失了.</p>
<p>同时,<code>散列表</code>的性能也依赖于<code>α=N/M</code>的比值,其中<code>α</code>称为<code>散列表</code>的使用率.对于<code>拉链法</code>来说,<code>α</code>是每条链表的长度,因此一般大于1.对于<code>线性探测法</code>来说,<code>α</code>是表中已被占用的空间的比例,它是不可能大于1的.</p>
<p><code>散列表</code>的性能虽然高效,但它也有以下的局限性: </p>
<ul>
<li>每种类型的键都需要一个优秀的<code>散列函数</code>.</li>
</ul>
<ul>
<li>性能保证来自于<code>散列函数</code>的质量.</li>
</ul>
<ul>
<li><code>散列函数</code>的计算可能复杂而且昂贵.</li>
</ul>
<ul>
<li>难以支持有序性相关的操作.</li>
</ul>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<ul>
<li>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></li>
</ul>
<ul>
<li>Email : <a href="mailto:&#115;&#121;&#108;&#x76;&#97;&#110;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#120;&#x74;&#122;&#x40;&#x31;&#54;&#x33;&#46;&#x63;&#111;&#109;">&#115;&#121;&#108;&#x76;&#97;&#110;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#120;&#x74;&#122;&#x40;&#x31;&#54;&#x33;&#46;&#x63;&#111;&#109;</a></li>
</ul>
<ul>
<li>文中的完整实现代码见我的<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/algs4-study">GitHub</a> &amp; <a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/6872abd0fad061de28466cb775a84cea">Gist</a></li>
</ul>
<ul>
<li>文中参考资料引用自<a target="_blank" rel="noopener" href="http://algs4.cs.princeton.edu/34hash/">&lt;&lt;Algorithms,4th Edition&gt;&gt;</a> &amp; <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Hash_table">Wikepedia</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/04/08/2017-4-08-avl_tree/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/04/08/2017-4-08-avl_tree/" class="post-title-link" itemprop="url">平衡查找树之AVL树</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-04-08 18:00:00" itemprop="dateCreated datePublished" datetime="2017-04-08T18:00:00+08:00">2017-04-08</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/Tree/" itemprop="url" rel="index"><span itemprop="name">Tree</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><hr>
<p><code>AVL树</code>得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,它是最先发明的<code>自平衡二叉查找树</code>.</p>
<p>在<code>AVL树</code>中<strong>任何节点的两个子树的高度最大差别为一</strong>.并且,查找、插入、删除等操作在平均和最坏情况下都是<code>O(log n)</code>.</p>
<p><code>AVL树</code>的基本操作都与<code>二叉查找树</code>的算法一致,只有在插入、删除等这种会<strong>改变树的平衡性的操作需要使用一些<code>旋转操作</code>来修正树的平衡性</strong>.</p>
<h2 id="平衡因子"><a href="#平衡因子" class="headerlink" title="平衡因子"></a>平衡因子</h2><hr>
<p>节点的<code>平衡因子</code>一般是它的<code>左子树</code>的高度减去它的<code>右子树</code>的高度(相反也可以).带有<code>平衡因子</code>为1、0或-1的节点被认为是平衡的.带有<code>平衡因子</code>为-2或2的节点被认为是不平衡的.</p>
<p>计算树的高度与平衡因子的代码如下.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// calculate node x depth</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">calcDepth</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> depth = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (x.left != <span class="keyword">null</span>)</span><br><span class="line">        depth = x.left.depth;</span><br><span class="line">    <span class="keyword">if</span> (x.right != <span class="keyword">null</span> &amp;&amp; x.right.depth &gt; depth)</span><br><span class="line">        depth = x.right.depth;</span><br><span class="line">    <span class="comment">// parent + left or right depth</span></span><br><span class="line">    depth++;</span><br><span class="line">    <span class="keyword">return</span> depth;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// calculate node x balance(left.depth - right.depth)</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">calcBalance</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> leftDepth = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> rightDepth = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (x.left != <span class="keyword">null</span>)</span><br><span class="line">        leftDepth = x.left.depth;</span><br><span class="line">    <span class="keyword">if</span> (x.right != <span class="keyword">null</span>)</span><br><span class="line">        rightDepth = x.right.depth;</span><br><span class="line">    <span class="keyword">return</span> leftDepth - rightDepth;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="旋转"><a href="#旋转" class="headerlink" title="旋转"></a>旋转</h2><hr>
<p><code>旋转操作</code>是用于修复树的平衡性的,它保证了树的有序性与平衡性(旋转操作的具体讲解可以参考<a target="_blank" rel="noopener" href="http://sylvanassun.github.io/2017/03/30/red_black_binary_search_tree/">《Algorithms,4th Edition》读书笔记-红黑二叉查找树</a>).</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">rotateLeft</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    Node t = x.right;</span><br><span class="line">    x.right = t.left;</span><br><span class="line">    t.left = x;</span><br><span class="line">    <span class="keyword">if</span> (x.parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">        t.parent = x.parent;</span><br><span class="line">        <span class="keyword">if</span> (x.parent.left == x)</span><br><span class="line">            x.parent.left = t;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            x.parent.right = t;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        t.parent = <span class="keyword">null</span>;</span><br><span class="line">        root = t;</span><br><span class="line">    &#125;</span><br><span class="line">    x.parent = t;</span><br><span class="line">    <span class="comment">// calculate depth and balance</span></span><br><span class="line">    x.depth = calcDepth(x);</span><br><span class="line">    x.balance = calcBalance(x);</span><br><span class="line">    t.depth = calcDepth(t);</span><br><span class="line">    t.balance = calcBalance(t);</span><br><span class="line">    <span class="comment">// calculate size</span></span><br><span class="line">    t.size = x.size;</span><br><span class="line">    x.size = <span class="number">1</span> + size(x.left) + size(x.right);</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">rotateRight</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    Node t = x.left;</span><br><span class="line">    x.left = t.right;</span><br><span class="line">    t.right = x;</span><br><span class="line">    <span class="keyword">if</span> (x.parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">        t.parent = x.parent;</span><br><span class="line">        <span class="keyword">if</span> (x.parent.left == x)</span><br><span class="line">            x.parent.left = t;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            x.parent.right = t;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        t.parent = <span class="keyword">null</span>;</span><br><span class="line">        root = t;</span><br><span class="line">    &#125;</span><br><span class="line">    x.parent = t;</span><br><span class="line">    <span class="comment">// calculate depth and balance</span></span><br><span class="line">    x.depth = calcDepth(x);</span><br><span class="line">    x.balance = calcBalance(x);</span><br><span class="line">    t.depth = calcDepth(t);</span><br><span class="line">    t.balance = calcBalance(t);</span><br><span class="line">    <span class="comment">// calculate size</span></span><br><span class="line">    t.size = x.size;</span><br><span class="line">    x.size = <span class="number">1</span> + size(x.left) + size(x.right);</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="平衡修正"><a href="#平衡修正" class="headerlink" title="平衡修正"></a>平衡修正</h2><hr>
<p>当一个节点被认为是不平衡的时候,我们需要使用一些<code>旋转操作</code>来修正树的平衡,一般有以下情况需要进行<code>旋转</code>.</p>
<ul>
<li>例如当前节点为<code>x</code>,对<code>x</code>进行平衡修正需要进行以下判断.</li>
</ul>
<ul>
<li>当<code>x</code>的<code>平衡因子</code>大于等于2时(左子树高度偏高),对其进行<code>右旋转</code>.</li>
</ul>
<ul>
<li>当<code>x</code>的<code>左子树</code>的<code>平衡因子</code>等于-1时(左子树的右子节点高度偏高),对<code>x</code>的<code>左子树</code>进行<code>左旋转</code>.</li>
</ul>
<ul>
<li>当<code>x</code>的<code>平衡因子</code>小于等于-2时(右子树高度偏高),对其进行<code>左旋转</code>.</li>
</ul>
<ul>
<li>当<code>x</code>的<code>右子树</code>的<code>平衡因子</code>等于1时(右子树的左子节点高度偏高),对<code>x</code>的<code>右子树</code>进行<code>右旋转</code>.</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">balance</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="keyword">null</span>) &#123;</span><br><span class="line">        x.depth = calcDepth(x);</span><br><span class="line">        x.balance = calcBalance(x);</span><br><span class="line">        <span class="comment">// if x left subtree high,rotateRight</span></span><br><span class="line">        <span class="keyword">if</span> (x.balance &gt;= <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="comment">// if x.left.right high,rotateLeft</span></span><br><span class="line">            <span class="keyword">if</span> (x.left != <span class="keyword">null</span> &amp;&amp; x.left.balance == -<span class="number">1</span>) &#123;</span><br><span class="line">                x.left = rotateLeft(x.left);</span><br><span class="line">            &#125;</span><br><span class="line">            x = rotateRight(x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// if x right subtree high,rotateLeft</span></span><br><span class="line">        <span class="keyword">if</span> (x.balance &lt;= -<span class="number">2</span>) &#123;</span><br><span class="line">            <span class="comment">// if x.right.left high,rotateRight</span></span><br><span class="line">            <span class="keyword">if</span> (x.right != <span class="keyword">null</span> &amp;&amp; x.right.balance == <span class="number">1</span>) &#123;</span><br><span class="line">                x.right = rotateRight(x.right);</span><br><span class="line">            &#125;</span><br><span class="line">            x = rotateLeft(x);</span><br><span class="line">        &#125;</span><br><span class="line">        x.size = <span class="number">1</span> + size(x.left) + size(x.right);</span><br><span class="line">        x = x.parent;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h2><hr>
<p><code>AVL树</code>的插入和删除与<code>二分查找树</code>的算法一致,只不过在完成插入后需要自底向上的修复平衡性.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called put() with key is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>) &#123;</span><br><span class="line">        remove(key);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    put(root, key, value);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(Node x, K key, V value)</span> </span>&#123;</span><br><span class="line">    Node parent = x;</span><br><span class="line">    <span class="keyword">int</span> cmp = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="keyword">null</span>) &#123;</span><br><span class="line">        parent = x;</span><br><span class="line">        cmp = key.compareTo(x.key);</span><br><span class="line">        <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            x = x.left;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            x = x.right;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            x.value = value;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// if not find key,create new node</span></span><br><span class="line">    x = <span class="keyword">new</span> Node(key, value, <span class="number">1</span>, parent);</span><br><span class="line">    <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>)</span><br><span class="line">            parent.left = x;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            parent.right = x;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        root = x;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// fixup balance</span></span><br><span class="line">    balance(x);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="end"><a href="#end" class="headerlink" title="end"></a>end</h2><hr>
<ul>
<li>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></li>
</ul>
<ul>
<li>Email : <a href="mailto:&#115;&#121;&#108;&#x76;&#97;&#110;&#x61;&#x73;&#x73;&#x75;&#x6e;&#95;&#120;&#x74;&#122;&#x40;&#x31;&#54;&#51;&#x2e;&#99;&#x6f;&#x6d;">&#115;&#121;&#108;&#x76;&#97;&#110;&#x61;&#x73;&#x73;&#x75;&#x6e;&#95;&#120;&#x74;&#122;&#x40;&#x31;&#54;&#51;&#x2e;&#99;&#x6f;&#x6d;</a></li>
</ul>
<ul>
<li>文中的完整实现代码见我的<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/algs4-study">GitHub</a> &amp; <a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/780045c5b8705ef225eb83c58a013949">Gist</a></li>
</ul>
<ul>
<li>本文参考资料引用自<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/AVL_tree">Wikipedia</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/03/30/2017-3-30-red_black_binary_search_tree/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/03/30/2017-3-30-red_black_binary_search_tree/" class="post-title-link" itemprop="url">《Algorithms,4th Edition》读书笔记-红黑二叉查找树</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-03-30 18:00:00" itemprop="dateCreated datePublished" datetime="2017-03-30T18:00:00+08:00">2017-03-30</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/Tree/" itemprop="url" rel="index"><span itemprop="name">Tree</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p><code>红黑二叉查找树</code>是<code>2-3查找树</code>的简单表示方式,它的代码量并不大,并且保证了平衡性.<br>阅读本文前需先了解 <a target="_blank" rel="noopener" href="http://sylvanassun.github.io/2017/03/28/2_3tree/">《Algorithms,4th Edition》读书笔记-2-3查找树</a></p>
</blockquote>
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><hr>
<p><code>红黑树</code>是一种自平衡的<code>二叉查找树</code>,它的基本思想是<strong>用标准的<code>二叉查找树</code>(完全由<code>2-节点</code>构成)和一些额外的信息(替换<code>3-节点</code>)来表示<code>2-3树</code>.</strong> 可以说<code>红黑树</code>是<code>2-3树</code>的一种等同.</p>
<p><code>红黑树</code>中的链接可以分为两种类型: </p>
<ul>
<li><strong>红链接</strong> : 它将两个<code>2-节点</code>连接起来构成一个<code>3-节点</code>(也可以说是将<code>3-节点</code>表示为由一条<strong>红色左链接</strong>(两个<code>2-节点</code>其中之一是另一个的左子节点)相连的两个<code>2-节点</code>).</li>
</ul>
<ul>
<li><strong>黑链接</strong> : 表示<code>2-3树</code>中的普通链接.</li>
</ul>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/redblack-encoding.png"></p>
<p>这种表示方式带来的优点如下: </p>
<ol>
<li>无需修改就可以直接使用标准的<code>二叉查找树</code>中的查找方法(其他与链接颜色不关联的方法也可以直接使用).</li>
</ol>
<ol start="2">
<li>对于任意的<code>2-3树</code>,只要对节点进行转换,我们都可以立即派生出一棵对应的<code>二叉查找树</code>.</li>
</ol>
<h2 id="红黑树的性质"><a href="#红黑树的性质" class="headerlink" title="红黑树的性质"></a>红黑树的性质</h2><hr>
<p><code>红黑树</code>是含有红黑链接并满足下列条件的<code>二叉查找树</code>(满足这些条件的<code>红黑树</code>才是与相应的<code>2-3树</code>一一对应的).</p>
<ul>
<li>红链接均为左链接(这条仅限于偏向左红链接实现的<code>红黑树</code>).</li>
</ul>
<ul>
<li>每个节点不是红色就是黑色的.</li>
</ul>
<ul>
<li>没有任何一个节点同时和两条红链接相连(不可以有两条连续的红链接).</li>
</ul>
<ul>
<li>该树是完美黑色平衡的,即<strong>任意空链接到根节点的路径上的黑链接数量相同.</strong></li>
</ul>
<ul>
<li><code>根节点</code>是黑色的.</li>
</ul>
<ul>
<li>所有<code>叶子节点</code>(即null节点)的颜色是黑色的.</li>
</ul>
<h2 id="与2-3树的对应关系"><a href="#与2-3树的对应关系" class="headerlink" title="与2-3树的对应关系"></a>与2-3树的对应关系</h2><hr>
<p>假如我们将一棵<code>红黑树</code>中的红链接画平,我们会发现所有的空链接到根节点的距离都将是相同的.如果再把由红链接相连的节点合并,得到的就是一棵<code>2-3树</code>.</p>
<p>相对的,如果将一棵<code>2-3树</code>中的<code>3-节点</code>画作由红色左链接相连的两个<code>2-节点</code>,那么不会存在能够和两条红链接相连的节点,且树必然是完美黑色平衡的,因为黑链接就是<code>2-3树</code>中的普通链接,根据定义这些链接必然是完美平衡的.</p>
<p>通过这些结论,我们<strong>可以发现<code>红黑树</code>即是<code>二叉查找树</code>,也是<code>2-3树</code>.</strong></p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/redblack-1-1.png"></p>
<h2 id="节点的实现"><a href="#节点的实现" class="headerlink" title="节点的实现"></a>节点的实现</h2><hr>
<p>我们使用<code>boolean</code>类型的变量<code>color</code>来表示链接的颜色.如果指向它的链接为红色,则<code>color</code>变量为<code>true</code>,黑色则为<code>false</code>(空链接也为黑色).</p>
<p>并且定义了一个<code>isRed()</code>函数用于判断链接的颜色.</p>
<p>这里节点的<strong>颜色指的是指向该节点的链接的颜色.</strong></p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/redblack-color.png"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> RED = <span class="keyword">true</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> BLACK = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> Node root; <span class="comment">// root node</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">      <span class="keyword">private</span> K key;</span><br><span class="line">      <span class="keyword">private</span> V value;</span><br><span class="line">      <span class="keyword">private</span> Node left, right; <span class="comment">// links to left and right subtress</span></span><br><span class="line">      <span class="keyword">private</span> <span class="keyword">boolean</span> color; <span class="comment">// color of parent link</span></span><br><span class="line">      <span class="keyword">private</span> <span class="keyword">int</span> size; <span class="comment">// subtree count</span></span><br><span class="line"></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(K key, V value, <span class="keyword">boolean</span> color, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">          <span class="keyword">this</span>.key = key;</span><br><span class="line">          <span class="keyword">this</span>.value = value;</span><br><span class="line">          <span class="keyword">this</span>.color = color;</span><br><span class="line">          <span class="keyword">this</span>.size = size;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">	</span><br><span class="line"><span class="comment">// node x is red? if x is null return false.</span></span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isRed</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">      <span class="keyword">return</span> x.color == RED;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<h2 id="旋转"><a href="#旋转" class="headerlink" title="旋转"></a>旋转</h2><hr>
<p>当我们在实现某些操作时,可能会产生一些红色右链接或者两条连续的红色左链接.这时就需要在操作完成前进行旋转操作来修复<code>红黑树</code>的平衡性(<strong>旋转操作会改变红链接的指向</strong>).</p>
<p>旋转操作保证了<code>红黑树</code>的两个重要性质 : <strong>有序性</strong>和<strong>完美平衡性</strong>. </p>
<h4 id="左旋转"><a href="#左旋转" class="headerlink" title="左旋转"></a>左旋转</h4><p>假设当前有一条红色右链接需要被修正旋转为左链接.这个操作叫做<code>左旋转</code>.</p>
<p><code>左旋转</code>函数接受一条指向<code>红黑树</code>中的某个节点的链接作为参数.然后<strong>会对树进行必要的调整并返回一个指向包含同一组键的子树且其左链接为红色的根节点的链接.</strong></p>
<p>也可以认为是<strong>将用两个键中的较小者作为根节点变为将较大者作为根节点</strong>(右旋转中逻辑相反).</p>
<p>旋转操作返回的链接可能是左链接也可能是右链接,这个链接可能是红色也可能是黑色的(在实现中我们使用<code>x.color = h.color</code>保留了它原本的颜色).这<strong>可能会产生两条连续的红链接,但算法会在后续操作中继续使用旋转操作修正这种情况.</strong></p>
<p><strong>旋转操作只影响了根节点</strong>(返回的节点的子树中的所有键和旋转前都相同,只有根节点发生了变化).</p>
<p>具体的实现如下图: </p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/redblack-left-rotate.png"></p>
<h4 id="右旋转"><a href="#右旋转" class="headerlink" title="右旋转"></a>右旋转</h4><p>实现<code>右旋转</code>的逻辑基本与<code>左旋转</code>相同,只需要将<code>left</code>和<code>right</code>互换即可.</p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/redblack-right-rotate.png"></p>
<h2 id="颜色转换"><a href="#颜色转换" class="headerlink" title="颜色转换"></a>颜色转换</h2><hr>
<p>颜色转换操作也是用于保证<code>红黑树</code>的性质的.<strong>它将<code>父节点</code>的颜色由黑变红,将<code>子节点</code>的颜色由红变黑.</strong></p>
<p>这项操作与旋转操作一样是局部变换,<strong>不会影响整棵树的黑色平衡性.</strong></p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/color-flip.png"></p>
<h4 id="根节点总是为黑"><a href="#根节点总是为黑" class="headerlink" title="根节点总是为黑"></a>根节点总是为黑</h4><p>颜色转换可能会使<code>根节点</code>变为红色,但红色的<code>根节点</code>说明<code>根节点</code>是一个<code>3-节点</code>的一部分,实际情况并不是这样的.所以我们需要将<code>根节点</code>设为黑色.</p>
<p><strong>每当<code>根节点</code>由红变黑时,树的黑链接高度就会加1.</strong></p>
<h2 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h2><hr>
<p>在<code>红黑树</code>中实现插入操作是比较复杂的,因为需要保持<code>红黑树</code>的平衡性.但只要利用好<code>左旋转</code>、<code>右旋转</code>、<code>颜色转换</code>这三个辅助操作,就能够保证插入操作后树的平衡性.</p>
<h4 id="向单个2-节点中插入新键"><a href="#向单个2-节点中插入新键" class="headerlink" title="向单个2-节点中插入新键"></a>向单个2-节点中插入新键</h4><p>当一棵只含有一个键的<code>红黑树</code>只含有一个<code>2-节点</code>时,插入另一个键后需要马上进行<code>旋转</code>操作修正树的平衡性.</p>
<ul>
<li>如果新键小于老键,只需要新增一个红色的节点即可(这时,新的<code>红黑树</code>等价于一个<code>3-节点</code>).</li>
</ul>
<ul>
<li>如果新键大于老键,那么新增的红色节点将会产生一条红色的右链接,这时就需要使用<code>左旋转</code>修正根节点的链接.</li>
</ul>
<ul>
<li>以上两种情况最终的结果均为一棵等价于单个<code>3-节点</code>的<code>红黑树</code>,它含有两个键,一条红链接,树的黑链接高度为1.</li>
</ul>
<h4 id="向树底部的2-节点插入新键"><a href="#向树底部的2-节点插入新键" class="headerlink" title="向树底部的2-节点插入新键"></a>向树底部的2-节点插入新键</h4><p>和<code>二叉查找树</code>一样,向<code>红黑树</code>中插入一个新键会在树的底部新增一个节点,但<strong>在<code>红黑树</code>中总是用红链接将新节点和它的父节点相连.</strong></p>
<p>如果它的父节点是一个<code>2-节点</code>,那么上一节讨论的方法依然适用.</p>
<ul>
<li>如果指向新节点的是父节点的左链接,那么父节点就直接成为一个<code>3-节点</code>.</li>
</ul>
<ul>
<li>如果指向新节点的是父节点的右链接,那么就需要一次<code>左旋转</code>进行修正.</li>
</ul>
<h4 id="向一棵双键树-一个3-节点-中插入新键"><a href="#向一棵双键树-一个3-节点-中插入新键" class="headerlink" title="向一棵双键树(一个3-节点)中插入新键"></a>向一棵双键树(一个3-节点)中插入新键</h4><p>当向一个<code>3-节点</code>中插入新键时,会发生以下三种情况且每种情况都会产生一个同时连接到两条红链接的节点,我们需要修正这一点.</p>
<ul>
<li>如果<code>新键大于原树中的两个键</code> : 这是最容易处理的一种情况,这个<strong>键会被连接到<code>3-节点</code>的右链接</strong>.此时树是平衡的,<strong>根节点为中间大小的键</strong>,它有<strong>两条红链接分别和较小和较大的节点相连</strong>.只需要<strong>把这两条链接的颜色都由红变黑,那么就可以得到一棵由三个节点组成、高度为2的平衡树</strong>(其他两种情况最终也会转化为这样的树).</li>
</ul>
<ul>
<li>如果<code>新键小于原树中的两个键</code> : 这个<strong>键会被连接到最左边的空链接,这样就产生了两条连续的红链接.**此时</strong>只需要将上层的红链接<code>右旋转</code>即可得到第一种情况**(中值键为根节点并和其他两个节点用红链接相连).</li>
</ul>
<ul>
<li>如果<code>新键介于原树中的两个键之间</code> : 这种情况依然<strong>会产生两条连续的红链接:一条红色左链接接一条红色右链接.**此时</strong>只需要将下层的红链接<code>左旋转</code>即可得到第二种情况**(两条连续的红色左链接).</li>
</ul>
<p>通过以上这三种情况可以总结出 : 我们只需要通过0次、1次、2次旋转以及颜色转换就可以完成对<code>红黑树</code>的修正.</p>
<h4 id="将红链接向上传递"><a href="#将红链接向上传递" class="headerlink" title="将红链接向上传递"></a>将红链接向上传递</h4><p>当每次旋转操作之后都会进行<code>颜色转换</code>,它会使得中间节点变为红色.<strong>从父节点的角度来看,处理这样一个红色节点的方式和处理一个新插入的红色节点完全相同</strong>(继续将红链接转移到中间节点).</p>
<p>这个操作对应于<code>2-3树</code>中向<code>3-节点</code>进行插入的操作 : 即在一个<code>3-节点</code>下插入新键,需要创建一个临时的<code>4-节点</code>,将其分解并将中间键插入父节点(在<code>红黑树</code>中,是将红链接由中间键传递给它的父节点).重复这个过程,直至遇到一个<code>2-节点</code>或者根节点.</p>
<p>当根节点变为红色时需要将根节点的颜色转换为黑色(对应<code>2-3树</code>中的根节点分解).</p>
<h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><p>插入操作的实现除了每次递归调用之后的对平衡性修正的操作,其他与<code>二叉查找树</code>中的插入操作没什么不同.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(K key, V val)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;first argument to put() is null&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (val == <span class="keyword">null</span>) &#123;</span><br><span class="line">        delete(key);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    root = put(root, key, val);</span><br><span class="line">    root.color = BLACK;</span><br><span class="line">    <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// insert the key-value pair in the subtree rooted at h</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">put</span><span class="params">(Node h, K key, V val)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (h == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> Node(key, val, RED, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> cmp = key.compareTo(h.key);</span><br><span class="line">    <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) h.left = put(h.left, key, val);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) h.right = put(h.right, key, val);</span><br><span class="line">    <span class="keyword">else</span> h.value = val;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// fix-up any right-leaning links</span></span><br><span class="line">    <span class="keyword">if</span> (isRed(h.right) &amp;&amp; !isRed(h.left)) h = rotateLeft(h);</span><br><span class="line">    <span class="keyword">if</span> (isRed(h.left) &amp;&amp; isRed(h.left.left)) h = rotateRight(h);</span><br><span class="line">    <span class="keyword">if</span> (isRed(h.left) &amp;&amp; isRed(h.right)) flipColors(h);</span><br><span class="line">    h.size = size(h.left) + size(h.right) + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> h;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>只要在沿着插入点到根节点的路径向上移动时<strong>在所经过的每个节点中顺序完成以下操作</strong>,就能够实现<code>红黑树</code>的插入操作.</p>
<ul>
<li>如果<code>右子节点</code>是红色的而<code>左子节点</code>是黑色的,那么进行<code>左旋转</code>.</li>
</ul>
<ul>
<li>如果<code>左子节点</code>是红色的而且它的<code>左子节点</code>也是红色的,那么进行<code>右旋转</code>.</li>
</ul>
<ul>
<li>如果<code>左右子节点</code>都是红色的,那么进行<code>颜色转换</code>.</li>
</ul>
<h2 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h2><hr>
<p>删除操作也需要定义一系列<code>局部变换</code>来在<strong>删除一个节点的同时保持树的完美平衡性</strong>.然而,这个过程要比插入操作还要复杂,它<strong>不仅要在(为了删除一个节点而)构造临时<code>4-节点</code>时沿着查找路径向下进行变换,还要在分解遗留的<code>4-节点</code>时沿着查找路径向上进行变换(同插入操作)</strong>.</p>
<h4 id="自顶向下的2-3-4树"><a href="#自顶向下的2-3-4树" class="headerlink" title="自顶向下的2-3-4树"></a>自顶向下的2-3-4树</h4><p><code>2-3-4树</code>是一种允许存在<code>4-节点</code>的树.它的插入算法就是一种沿着查找路径既能向上也能向下进行变换的算法.</p>
<ul>
<li>沿查找路径向下进行变换(向下变换与<code>2-3树</code>中分解<code>4-节点</code>所进行的变换完全相同)是为了保证当前节点不是<code>4-节点</code>(这样树的底部才有足够的空间插入新的键).</li>
</ul>
<ul>
<li>沿查找路径向上进行变换是为了将之前创建的<code>4-节点</code>配平.</li>
</ul>
<ul>
<li>如果<code>根节点</code>是一个<code>4-节点</code>,就将它分解成三个<code>2-节点</code>,树的高度加1.</li>
</ul>
<ul>
<li>如果在向下查找的过程中,遇到了一个<code>父节点</code>为<code>2-节点</code>的<code>4-节点</code>,就将<code>4-节点</code>分解为两个<code>2-节点</code>并将<code>中间键</code>传递给它的<code>父节点</code>(这时<code>父节点</code>变为了一个<code>3-节点</code>).</li>
</ul>
<ul>
<li>如果遇到了一个<code>父节点</code>为<code>3-节点</code>的<code>4-节点</code>,将<code>4-节点</code>分解为两个<code>2-节点</code>并将<code>中间键</code>传递给它的<code>父节点</code>(这时<code>父节点</code>变为了一个<code>4-节点</code>).</li>
</ul>
<ul>
<li>不必担心遇见<code>父节点</code>为<code>4-节点</code>的<code>4-节点</code>,算法本身保证了不会出现这种情况,到达树的底部之后,只会遇到<code>2-节点</code>或者<code>3-节点</code>.</li>
</ul>
<p>如果要使用<code>红黑树</code>来实现这个算法,需要以下步骤 : </p>
<ul>
<li>将<code>4-节点</code>表示为由三个<code>2-节点</code>组成的一棵平衡的子树,<code>根节点</code>和两个子节点都用红链接相连.</li>
</ul>
<ul>
<li>在向下的过程中分解所有<code>4-节点</code>并进行<code>颜色转换</code>.</li>
</ul>
<ul>
<li>在向上的过程中使用<code>旋转</code>将<code>4-节点</code>配平.</li>
</ul>
<p>只需要将插入一节中的<code>put()</code>实现方法里的<code>flipColors</code>语句(及其if语句)移动到递归调用之前(null判断和比较操作之间)就能实现<code>2-3-4树</code>的插入操作.</p>
<h4 id="删除最小键"><a href="#删除最小键" class="headerlink" title="删除最小键"></a>删除最小键</h4><p>从<code>2-节点</code>中删除一个键会留下一个空节点,一般会将它替换为一个空链接,但这样会破坏树的完美平衡性.所以在删除操作中,<strong>为了避免删除一个<code>2-节点</code>,我们沿着<code>左链接</code>向下进行变换时,需要确保当前节点不是<code>2-节点</code></strong>.</p>
<p><code>根节点</code>可能有以下两种情况:</p>
<ol>
<li>如果<code>根节点</code>是一个<code>2-节点</code>且它的两个子节点都是<code>2-节点</code>,可以直接将这三个节点变成一个<code>4-节点</code>.</li>
</ol>
<ol start="2">
<li>否则,需要保证<code>根节点</code>的左子节点不是<code>2-节点</code>,必要时可以从它右侧的兄弟节点借走一个键.</li>
</ol>
<p>在沿着<code>左链接</code>向下的过程中,保证以下情况之一成立: </p>
<ul>
<li>如果当前节点的左子节点不是<code>2-节点</code>.</li>
</ul>
<ul>
<li>如果当前节点的左子节点是<code>2-节点</code>而它的兄弟节点不是<code>2-节点</code>,将左子节点的兄弟节点中的一个键移动到左子节点中</li>
</ul>
<ul>
<li>如果当前节点的左子节点和它的兄弟节点都是<code>2-节点</code>,将左子节点、父节点中的最小键和左子节点最近的兄弟节点合并为一个<code>4-节点</code>,使父节点由<code>3-节点</code>变为<code>2-节点</code>(或是从<code>4-节点</code>变为<code>3-节点</code>).</li>
</ul>
<p>只要保证了以上的条件,我们最终能够得到一个含有最小键的<code>3-节点</code>或<code>4-节点</code>(然后进行删除即可),之后再不断向上分解所有临时的<code>4-节点</code>.</p>
<h5 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h5><p>在删除操作中,<code>颜色转换</code>的操作与插入操作中的实现略微有些不同(需要将父节点设为黑,而将两个子节点设为红).</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">flipColors</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">       h.color = !h.color;</span><br><span class="line">       h.left.color = !h.left.color;</span><br><span class="line">       h.right.color = !h.right.color;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// restore red-black tree invariant</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">balance</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (isRed(h.right)) h = rotateLeft(h);</span><br><span class="line">       <span class="keyword">if</span> (isRed(h.left) &amp;&amp; isRed(h.left.left)) h = rotateRight(h);</span><br><span class="line">       <span class="keyword">if</span> (isRed(h.left) &amp;&amp; isRed(h.right)) flipColors(h);</span><br><span class="line"></span><br><span class="line">       h.size = size(h.left) + size(h.right) + <span class="number">1</span>;</span><br><span class="line">       <span class="keyword">return</span> h;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// Assuming that h is red and both h.left and h.left.left</span></span><br><span class="line">   <span class="comment">// are black, make h.left or one of its children red.</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">moveRedLeft</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">       flipColors(h);</span><br><span class="line">       <span class="keyword">if</span> (isRed(h.right.left)) &#123;</span><br><span class="line">           h.right = rotateRight(h.right);</span><br><span class="line">           h = rotateLeft(h);</span><br><span class="line">           flipColors(h);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> h;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteMin</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (isEmpty()) <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;RedBlackBST underflow.&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// if both children of root are black, set root to red</span></span><br><span class="line">       <span class="keyword">if</span> (!isRed(root.left) &amp;&amp; !isRed(root.right))</span><br><span class="line">           root.color = RED;</span><br><span class="line"></span><br><span class="line">       root = deleteMin(root);</span><br><span class="line">       <span class="keyword">if</span> (!isEmpty())</span><br><span class="line">           root.color = BLACK;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// delete the key-value pair with the minimum key rooted at h</span></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">deleteMin</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (h.left == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (!isRed(h.left) &amp;&amp; !isRed(h.left.left))</span><br><span class="line">           h = moveRedLeft(h);</span><br><span class="line"></span><br><span class="line">       h.left = deleteMin(h.left);</span><br><span class="line">       <span class="keyword">return</span> balance(h);</span><br><span class="line">   &#125;	</span><br></pre></td></tr></table></figure>
<h4 id="删除最大键"><a href="#删除最大键" class="headerlink" title="删除最大键"></a>删除最大键</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Assuming that h is red and both h.right and h.right.left</span></span><br><span class="line"><span class="comment">// are black, make h.right or one of its children red.</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">moveRedRight</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">    flipColors(h);</span><br><span class="line">    <span class="keyword">if</span> (isRed(h.left.left)) &#123;</span><br><span class="line">        h = rotateRight(h);</span><br><span class="line">        flipColors(h);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> h;</span><br><span class="line">&#125;</span><br><span class="line">	</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteMax</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (isEmpty()) <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;RedBlackBST underflow.&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// if both children of root are black, set root to red</span></span><br><span class="line">    <span class="keyword">if</span> (!isRed(root.left) &amp;&amp; !isRed(root.right))</span><br><span class="line">        root.color = RED;</span><br><span class="line"></span><br><span class="line">    root = deleteMax(root);</span><br><span class="line">    <span class="keyword">if</span> (!isEmpty())</span><br><span class="line">        root.color = BLACK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// delete the key-value pair with the maximum key rooted at h</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">deleteMax</span><span class="params">(Node h)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (isRed(h.left))</span><br><span class="line">        h = rotateRight(h);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (h.right == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!isRed(h.right) &amp;&amp; !isRed(h.right.left))</span><br><span class="line">        h = moveRedRight(h);</span><br><span class="line"></span><br><span class="line">    h.right = deleteMax(h.right);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> balance(h);</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<h4 id="删除操作"><a href="#删除操作" class="headerlink" title="删除操作"></a>删除操作</h4><p>同样也需要像删除最小键那样在查找路径上进行变换来保证查找过程中任意当前节点均不是<code>2-节点</code>.如果目标键在树的底部,可以直接删除它;如果不在,则需要将它和它的后继节点交换.</p>
<p>在删除操作之后需要向上变换分解余下的<code>4-节点</code>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called delete() with key is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!contains(key)) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// if both children of root are black, set root to red</span></span><br><span class="line">    <span class="keyword">if</span> (!isRed(root.left) &amp;&amp; !isRed(root.right))</span><br><span class="line">        root.color = RED;</span><br><span class="line"></span><br><span class="line">    root = delete(root, key);</span><br><span class="line">    <span class="keyword">if</span> (!isEmpty()) root.color = BLACK;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// delete the key-value pair with the given key rooted at h</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">delete</span><span class="params">(Node h, K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key.compareTo(h.key) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!isRed(h.left) &amp;&amp; !isRed(h.left.left))</span><br><span class="line">            h = moveRedLeft(h);</span><br><span class="line">        h.left = delete(h.left, key);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isRed(h.left))</span><br><span class="line">            h = rotateRight(h);</span><br><span class="line">        <span class="keyword">if</span> (key.compareTo(h.key) == <span class="number">0</span> &amp;&amp; (h.right == <span class="keyword">null</span>))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (!isRed(h.right) &amp;&amp; !isRed(h.right.left))</span><br><span class="line">            h = moveRedRight(h);</span><br><span class="line">        <span class="keyword">if</span> (key.compareTo(h.key) == <span class="number">0</span>) &#123;</span><br><span class="line">            Node x = min(h.right);</span><br><span class="line">            h.key = x.key;</span><br><span class="line">            h.value = x.value;</span><br><span class="line">            h.right = deleteMin(h.right);</span><br><span class="line">        &#125; <span class="keyword">else</span> h.right = delete(h.right, key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> balance(h);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><hr>
<p>无论键的插入顺序如何,<code>红黑树</code>都几乎是完美平衡的,基于它实现的有序符号表操作的运行时间均为对数级别(除了范围查询).</p>
<p>在<code>红黑树</code>的实现中复杂的代码仅限于<code>put()</code>和<code>delete()</code>方法,像<code>get()</code>这些不会涉及检查颜色的方法与<code>二叉查找树</code>中的实现一致(因为这些操作与平衡性无关).</p>
<h2 id="end"><a href="#end" class="headerlink" title="end"></a>end</h2><hr>
<ul>
<li>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></li>
</ul>
<ul>
<li>Email : <a href="mailto:&#115;&#121;&#108;&#x76;&#x61;&#x6e;&#97;&#115;&#115;&#x75;&#110;&#x5f;&#x78;&#x74;&#122;&#x40;&#49;&#x36;&#51;&#x2e;&#x63;&#x6f;&#109;">&#115;&#121;&#108;&#x76;&#x61;&#x6e;&#97;&#115;&#115;&#x75;&#110;&#x5f;&#x78;&#x74;&#122;&#x40;&#49;&#x36;&#51;&#x2e;&#x63;&#x6f;&#109;</a></li>
</ul>
<ul>
<li>文中的完整实现代码见我的<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/algs4-study">GitHub</a> &amp; <a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/731a1438c61492628cfaa1e9e618ecfb">Gist</a></li>
</ul>
<ul>
<li>本文参考资料引用自<a target="_blank" rel="noopener" href="http://algs4.cs.princeton.edu/33balanced/">《Algorithms,4th Editio》</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/03/28/2017-3-28-2_3tree/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/03/28/2017-3-28-2_3tree/" class="post-title-link" itemprop="url">《Algorithms,4th Edition》读书笔记-2-3查找树</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-03-28 18:00:00" itemprop="dateCreated datePublished" datetime="2017-03-28T18:00:00+08:00">2017-03-28</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/Tree/" itemprop="url" rel="index"><span itemprop="name">Tree</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>由于<code>二叉查找树</code>的性能与树的高度(即根节点到底部节点的深度)相关,因此当高度较大时,<code>二叉查找树</code>的性能就会下降.为了更高效的性能,<code>平衡查找树</code>应运而生,它能保证<strong>无论键的插入顺序如何,树的高度都将是总键数的对数.</strong></p>
<p><code>2-3查找树</code>就是平衡树的一种.</p>
<h3 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h3><hr>
<p><code>2-3查找树</code><strong>允许树中的一个节点保存多个键.**我们可以将<code>二叉查找树</code>中的节点称为<code>2-节点</code>,而在<code>2-3查找树</code>中引入了<code>3-节点</code>,它</strong>含有两个键和三条链接.**</p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-anatomy.png" alt="2-3查找树"></p>
<blockquote>
<p>一棵<code>2-3查找树</code>由以下节点组成: </p>
<ul>
<li><p><code>2-节点</code> : 含有一个键(及其对应的值)和两条链接,左链接指向的<code>2-3查找树</code>中的键都小于该节点,右链接指向的<code>2-3查找树</code>中的键都大于该节点.</p>
</li>
<li><p><code>3-节点</code> : 含有两个键(及其对应的值)和三条链接,左链接指向的<code>2-3查找树</code>中的键都小于该节点,<strong>中链接指向的<code>2-3查找树</code>中的键都位于该节点的两个键之间</strong>,右链接指向的<code>2-3查找树</code>中的键都大于该节点.</p>
</li>
</ul>
</blockquote>
<p>一棵完美平衡的<code>2-3查找树</code>中的<strong>所有空链接到根节点的距离都应该是相同的.</strong></p>
<h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><hr>
<p><code>2-3查找树</code>的查找算法与<code>二叉查找树</code>基本相似.</p>
<ul>
<li>首先,要判断一个键需要先将它和根节点中的键进行比较.</li>
</ul>
<ul>
<li>如果它和其中任意一个相等,查找命中.</li>
</ul>
<ul>
<li>否则,根据比较的结果找到指向相应区间的链接,并在其指向的子树中递归地继续查找.</li>
</ul>
<ul>
<li>如果最后指向空链接,查找未命中.</li>
</ul>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-search.png" alt="2-3树查找操作的路径轨迹"></p>
<h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><hr>
<p>由于<code>2-3查找树</code>需要保持完美平衡性,所以它的插入算法并不像<code>二叉查找树</code>那么简单.</p>
<p>它的插入算法基本思想是 : <strong>一直向上不断分解临时的<code>4-节点</code>并将中键插入更高层的父节点中,直至遇到一个<code>2-节点</code>并将它替换为一个不需要继续分解的<code>3-节点</code>,或是到达<code>3-节点</code>的根(分解根节点)</strong></p>
<h4 id="向2-节点中插入新键"><a href="#向2-节点中插入新键" class="headerlink" title="向2-节点中插入新键"></a>向2-节点中插入新键</h4><p>如果未命中的查找结束于一个<code>2-节点</code>,只需要把这个<code>2-</code>节点替换为一个<code>3-节点</code>,将要插入的键保存在其中即可.</p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-insert2.png"></p>
<h4 id="向一棵只含有一个3-节点的树中插入新键"><a href="#向一棵只含有一个3-节点的树中插入新键" class="headerlink" title="向一棵只含有一个3-节点的树中插入新键"></a>向一棵只含有一个3-节点的树中插入新键</h4><p>如果我们需要向一棵只含有一个<code>3-节点</code>的树中插入一个新键(这棵树中唯一的节点已经没有可插入新键的空间了).</p>
<ol>
<li>先临时将新键存入该节点中,使之成为一个<code>4-节点</code>(它扩展了以前的节点并含有3个键和4条链接).</li>
</ol>
<ol start="2">
<li>将<code>4-节点</code>分解为一棵由3个<code>2-</code>节点组成的<code>2-3查找树</code>,其中一个节点(根)含有中键,一个节点含有3个键中的最小者(和根节点的左链接相连),一个节点含有3个键中的最大者(和根节点的右链接相连).</li>
</ol>
<ol start="3">
<li>这时,这棵树既是一棵含有3个节点的<code>二叉查找树</code>,同时也是一棵完美平衡的<code>2-3查找树</code>.</li>
</ol>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-insert3a.png"></p>
<h4 id="向一个父节点为2-节点的3-节点中插入新键"><a href="#向一个父节点为2-节点的3-节点中插入新键" class="headerlink" title="向一个父节点为2-节点的3-节点中插入新键"></a>向一个父节点为2-节点的3-节点中插入新键</h4><p>如果未命中的查找结束于一个<code>3-节点</code>,而它的父节点是一个<code>2-节点</code>.这种情况下,我们需要在<strong>维持树的完美平衡性的前提下为新键腾出空间.</strong></p>
<ol>
<li>构造一个临时的<code>4-节点</code>并将其分解(此时并不会为中键创建一个新节点).</li>
</ol>
<ol start="2">
<li>将中键移动至父节点中(可以看做将指向<code>3-节点</code>的一条链接替换为新父节点中的原中键左右两边的两条链接,并分别指向两个新的<code>2-节点</code>).</li>
</ol>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-insert3b.png"></p>
<h4 id="向一个父节点为3-节点的3-节点中插入新键"><a href="#向一个父节点为3-节点的3-节点中插入新键" class="headerlink" title="向一个父节点为3-节点的3-节点中插入新键"></a>向一个父节点为3-节点的3-节点中插入新键</h4><p>如果未命中的查找结束于一个父节点为<code>3-节点</code>且它本身也是一个<code>3-节点</code>时.我们可以构造一个临时的<code>4-节点</code>并分解它.将中键插入到它的父节点中.</p>
<p>但由于它的父节点也是一个<code>3-节点</code>,所以需要再用这个中键构造一个新的临时<code>4-节点</code>,然后在这个节点上进行相同的变换,即分解这个父节点并将它的中键插入到它的父节点中.</p>
<p>重复相同的变换直到遇到一个<code>2-节点</code>(将<code>2-节点</code>替换为一个<code>3-节点</code>)或者到达根节点(分解根节点).</p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-insert3c.png"></p>
<h4 id="分解根节点"><a href="#分解根节点" class="headerlink" title="分解根节点"></a>分解根节点</h4><p>如果从<strong>插入节点到根节点的路径上全部都是<code>3-节点</code></strong>.那么根节点最终会变成一个临时的<code>4-节点</code>,这时可以将<code>4-节点</code>分解为3个<code>2-节点</code>,同时树高加1(仍然保持了树的完美平衡性,因为它变换的是根节点).</p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-split.png"></p>
<h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><hr>
<p>关于如何使用一个简单的数据结构来表达实现<code>2-3查找树</code>可以见此文  <a target="_blank" rel="noopener" href="http://sylvanassun.github.io/2017/03/29/red_black_binary_search_tree/">&lt;&lt;Algorithms,4th Edition&gt;&gt;读书笔记-红黑二叉查找树</a> </p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><hr>
<p><code>2-3查找树</code>的根本在于<strong>插入操作中的变换操作都是局部的</strong>,除了相关的节点和链接之外不必修改或者检查树的其他部分.</p>
<p>每次变换都会将<code>4-节点</code>中的一个键移动至它的父节点中,并重构相应的链接而不必涉及树的其他部分.且保持了树的完美平衡性,例如在变换之前根节点到所有空链接的路径长度为<code>h</code>,那么变换之后该长度仍然为<code>h</code>.<strong>只有进行根节点分解时,所有空链接到根节点的路径长度才会加1.</strong></p>
<p><img src="http://algs4.cs.princeton.edu/33balanced/images/23tree-random.png"></p>
<p>通过这些我们可以总结得出: <strong><code>2-3查找树</code>的生长是由下向上的.</strong> (标准的<code>二叉查找树</code>则是由上向下生长的)</p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<ul>
<li><p>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></p>
</li>
<li><p>Email : <a href="mailto:&#x73;&#x79;&#x6c;&#x76;&#x61;&#x6e;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#x78;&#116;&#x7a;&#x40;&#49;&#54;&#x33;&#x2e;&#99;&#111;&#109;">&#x73;&#x79;&#x6c;&#x76;&#x61;&#x6e;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#x78;&#116;&#x7a;&#x40;&#49;&#54;&#x33;&#x2e;&#99;&#111;&#109;</a></p>
</li>
<li><p>本文参考资料引用自<a target="_blank" rel="noopener" href="http://algs4.cs.princeton.edu/33balanced/">&lt;&lt;Algorithms,4th Edition&gt;&gt;</a></p>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/03/26/2017-3-26-binary_search_tree/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/03/26/2017-3-26-binary_search_tree/" class="post-title-link" itemprop="url">     《Algorithms,4th Edition》读书笔记-二叉查找树</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-03-26 18:00:00" itemprop="dateCreated datePublished" datetime="2017-03-26T18:00:00+08:00">2017-03-26</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/Tree/" itemprop="url" rel="index"><span itemprop="name">Tree</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><hr>
<p>二叉查找树是一颗<strong>有序的二叉树</strong>,它有以下性质:</p>
<ol>
<li>若任意节点的左子树不为空,则<strong>左子树上所有节点的值均小于它的根节点的值</strong>.</li>
</ol>
<ol start="2">
<li>若任意节点的右子树不为空,则<strong>右子树上所有节点的值均大于它的根节点的值</strong>.</li>
</ol>
<ol start="3">
<li>可以将<strong>每个链接看做指向另一颗二叉查找树</strong>,而这棵树的根节点就是被指向的节点.</li>
</ol>
<ol start="4">
<li>没有键值相等的节点.</li>
</ol>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/binary-tree-anatomy.png"> </p>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/bst-anatomy.png"></p>
<p>使用二叉查找树实现的符号表结合了<strong>链表插入的灵活性和有序数组查找的高效性</strong>.通常采取二叉链表作为存储结构.</p>
<p>每个节点都含有一个键、一个值、一条左链接、一条右链接和一个节点计数器(用于统计其所有子节点数).<strong>左链接指向一棵由小于该节点的所有键组成的二叉查找树,右链接指向一棵由大于该节点的所有键组成的二叉查找树.</strong></p>
<p>如果将一棵二叉查找树的所有键投影到一条直线上,保证一个节点的<strong>左子树中的键出现在它的左边</strong>,<strong>右子树种的键出现在它的右边</strong>,那么我们一定<strong>可以得到一条有序的键列</strong>.</p>
<p>可以说二叉查找树和快速排序很相似.树的根节点就是快速排序中的第一个<code>基准数(切分元素)</code>,左侧的键都比它小,右侧的键都比它大.</p>
<h3 id="基本实现"><a href="#基本实现" class="headerlink" title="基本实现"></a>基本实现</h3><hr>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span>&lt;<span class="title">K</span> <span class="keyword">extends</span> <span class="title">Comparable</span>&lt;<span class="title">K</span>&gt;, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node root; <span class="comment">// root node</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> K key;</span><br><span class="line">        <span class="keyword">private</span> V value;</span><br><span class="line">        <span class="keyword">private</span> Node left, right; <span class="comment">// left and right subtree</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> size; <span class="comment">// number of nodes in subtree</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(K key, V value, <span class="keyword">int</span> size)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">            <span class="keyword">this</span>.size = size;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns true if this symbol table is empty.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &#123;<span class="doctag">@code</span> true&#125; is this symbol table is empty, &#123;<span class="doctag">@code</span> false&#125; otherwise.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> size() == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the number of key-value pairs in this symbol table.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the number of key-value pairs in this symbol table.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> size(root);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// return number of key-value pairs in binary search tree rooted at x</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">return</span> x.size;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<p>在以上代码中,使用私有嵌套类<code>Node</code>来表示一个二叉链表,每个<code>Node</code>对象都是一棵含有N个节点的子树的根节点.变量<code>root</code>指向二叉查找树的根节点(这棵树包含了符号表中的所有键值对).</p>
<h3 id="查找与插入"><a href="#查找与插入" class="headerlink" title="查找与插入"></a>查找与插入</h3><hr>
<h4 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * Returns the value associated with the given key.</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> key the key</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span> the value associated with the given key if the key is in the symbol table</span></span><br><span class="line"><span class="comment">    * and &#123;<span class="doctag">@code</span> null&#125; if the key is not in the symbol table.</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@throws</span> IllegalArgumentException if &#123;<span class="doctag">@code</span> key&#125; is &#123;<span class="doctag">@code</span> null&#125;</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called get() with a null key.&quot;</span>);</span><br><span class="line">       <span class="keyword">return</span> get(root, key);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">private</span> V <span class="title">get</span><span class="params">(Node x, K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">       <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">       <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="comment">// if key &lt; x.key , search left subtree</span></span><br><span class="line">           <span class="keyword">return</span> get(x.left, key);</span><br><span class="line">       &#125; <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) &#123;</span><br><span class="line">           <span class="comment">// if key &gt; x.key , search right subtree</span></span><br><span class="line">           <span class="keyword">return</span> get(x.right, key);</span><br><span class="line">       &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">           <span class="comment">// hit target</span></span><br><span class="line">           <span class="keyword">return</span> x.value;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<p>在二叉查找树中实现查找操作是很简单而简洁的,这也是二叉查找树的特性之一.-<strong>如果树为空,就返回null,如果被查找的键小于根节点的键,我们就继续在左子树中查找,否则在右子树中查找.</strong></p>
<h4 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h4><p>插入操作的逻辑与查找差不多,只不过需要在<strong>判定树为空时,返回一个含有该键值对的新节点,还需要重置搜索路径上每个父节点指向子节点的链接,并增加路径上每个节点中的子节点计数器的值</strong>.</p>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/bst-insert.png"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Inserts the specified key-value pair into  the symbol table.</span></span><br><span class="line"><span class="comment">     * overwriting the old value with the new value if the symbol table already contains</span></span><br><span class="line"><span class="comment">     * the specified key.</span></span><br><span class="line"><span class="comment">     * Deletes the specified key (and its associated value) from this symbol table</span></span><br><span class="line"><span class="comment">     * if the specified value is &#123;<span class="doctag">@code</span> null&#125;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   the key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value the value</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException if &#123;<span class="doctag">@code</span> key&#125; is &#123;<span class="doctag">@code</span> null&#125;.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called put() with a null key.&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (value == <span class="keyword">null</span>) &#123;</span><br><span class="line">            delete(key);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        root = put(root, key, value);</span><br><span class="line">        <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Node <span class="title">put</span><span class="params">(Node x, K key, V value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// if tree is empty, return a new node.</span></span><br><span class="line">        <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> Node(key, value, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">        <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            x.left = put(x.left, key, value);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            x.right = put(x.right, key, value);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// hit target,overwriting old value with the new value</span></span><br><span class="line">            x.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">        x.size = <span class="number">1</span> + size(x.left) + size(x.right); <span class="comment">// compute subtree node size</span></span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<h3 id="最大键和最小键"><a href="#最大键和最小键" class="headerlink" title="最大键和最小键"></a>最大键和最小键</h3><hr>
<ul>
<li>如果根节点的<strong>左链接为空</strong>,那么一棵二叉查找树中<strong>最小的键就是根节点</strong>.</li>
</ul>
<ul>
<li>如果<strong>左链接非空</strong>,那么树中的<strong>最小键就是左子树中的最小键</strong>.找出最大键的逻辑也是类似的,只是变为查找右子树而已.</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * Returns the smallest key in the symbol table.</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@return</span> the smallest key in the symbol table.</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@throws</span> NoSuchElementException if the symbol table is empty</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> K <span class="title">min</span><span class="params">()</span> </span>&#123;</span><br><span class="line">     <span class="keyword">if</span> (isEmpty())</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called min() with empty symbol table.&quot;</span>);</span><br><span class="line">     <span class="keyword">return</span> min(root).key;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> Node <span class="title">min</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">if</span> (x.left == <span class="keyword">null</span>) <span class="keyword">return</span> x;</span><br><span class="line">     <span class="keyword">else</span> <span class="keyword">return</span> min(x.left);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * Returns the largest key in the symbol table.</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@return</span> the largest key in the symbol table.</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@throws</span> NoSuchElementException if the symbol table is empty</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> K <span class="title">max</span><span class="params">()</span> </span>&#123;</span><br><span class="line">     <span class="keyword">if</span> (isEmpty())</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called max() with empty symbol table.&quot;</span>);</span><br><span class="line">     <span class="keyword">return</span> max(root).key;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> Node <span class="title">max</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">if</span> (x.right == <span class="keyword">null</span>) <span class="keyword">return</span> x;</span><br><span class="line">     <span class="keyword">else</span> <span class="keyword">return</span> max(x.right);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
<h3 id="向上取整和向下取整"><a href="#向上取整和向下取整" class="headerlink" title="向上取整和向下取整"></a>向上取整和向下取整</h3><hr>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/bst-floor.png" alt="向下取整的路径轨迹"></p>
<ul>
<li>如果给定的键<strong>key小于二叉查找树的根节点的键</strong>,那么小于等于key的最大键<code>floor(key)</code><strong>一定在根节点的左子树中</strong>.</li>
</ul>
<ul>
<li>如果给定的键<strong>key大于二叉查找树的根节点</strong>,那么<strong>只有当根节点右子树中存在小于等于key的节点时,小于等于key的最大键才会出现在右子树中,否则根节点就是小于等于key的最大键</strong>.(将左变为右,小于变为大于就是向上取整的实现逻辑)</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> K <span class="title">floor</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called floor() with a null key.&quot;</span>);</span><br><span class="line">       <span class="keyword">if</span> (isEmpty())</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called floor() with empty symbol table.&quot;</span>);</span><br><span class="line">       Node x = floor(root, key);</span><br><span class="line">       <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">       <span class="keyword">else</span> <span class="keyword">return</span> x.key;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">floor</span><span class="params">(Node x, K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">       <span class="keyword">if</span> (cmp == <span class="number">0</span>) <span class="keyword">return</span> x;</span><br><span class="line">       <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) <span class="keyword">return</span> floor(x.left, key);</span><br><span class="line">       Node t = floor(x.right, key);</span><br><span class="line">       <span class="keyword">if</span> (t != <span class="keyword">null</span>) <span class="keyword">return</span> t;</span><br><span class="line">       <span class="keyword">else</span> <span class="keyword">return</span> x;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> K <span class="title">ceiling</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called ceiling() with a null key.&quot;</span>);</span><br><span class="line">       <span class="keyword">if</span> (isEmpty())</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;called ceiling() with empty symbol table.&quot;</span>);</span><br><span class="line">       Node x = ceiling(root, key);</span><br><span class="line">       <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">       <span class="keyword">else</span> <span class="keyword">return</span> x.key;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">private</span> Node <span class="title">ceiling</span><span class="params">(Node x, K key)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">       <span class="keyword">if</span> (cmp == <span class="number">0</span>) <span class="keyword">return</span> x;</span><br><span class="line">       <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">           Node t = ceiling(x.left, key);</span><br><span class="line">           <span class="keyword">if</span> (t != <span class="keyword">null</span>) <span class="keyword">return</span> t;</span><br><span class="line">           <span class="keyword">else</span> <span class="keyword">return</span> x;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> ceiling(x.right, key);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
<h3 id="选择和排名"><a href="#选择和排名" class="headerlink" title="选择和排名"></a>选择和排名</h3><hr>
<h4 id="select"><a href="#select" class="headerlink" title="select"></a>select</h4><p>假设我们想找到排名为<code>k</code>的键(即树中正好有k个小于它的键).</p>
<ul>
<li>如果<strong>左子树中的节点数<code>t</code>大于<code>k</code></strong>,那么我们就继续递归地<strong>在左子树中查找排名为<code>k</code>的键</strong>.</li>
</ul>
<ul>
<li><strong>如果<code>t</code>等于<code>k</code></strong>,我们就<strong>返回根节点中的键</strong>.</li>
</ul>
<ul>
<li><strong>如果<code>t</code>小于<code>k</code></strong>,我们就递归地<strong>在右子树中查找排名为<code>(k-t-1)</code>的键</strong>.</li>
</ul>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/bst-select.png" alt="选择操作路径轨迹"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> K <span class="title">select</span><span class="params">(<span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (k &lt; <span class="number">0</span> || k &gt;= size())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called select() with invalid argument: &quot;</span> + k);</span><br><span class="line">    <span class="keyword">return</span> select(root, k).key;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">select</span><span class="params">(Node x, <span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> t = size(x.left);</span><br><span class="line">    <span class="comment">// if left subtree node size greater than k,in left subtree search</span></span><br><span class="line">    <span class="keyword">if</span> (t &gt; k) <span class="keyword">return</span> select(x.left, k);</span><br><span class="line">        <span class="comment">// otherwise,in right subtree search</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (t &lt; k) <span class="keyword">return</span> select(x.right, k - t - <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="rank"><a href="#rank" class="headerlink" title="rank"></a>rank</h4><p><code>rank()</code>函数是<code>select()</code>函数的逆函数,它会返回指定键的排名.</p>
<ul>
<li>如果给定的<strong>键和根节点的键相等</strong>,就<strong>返回左子树中的节点总数t</strong>.</li>
</ul>
<ul>
<li>如果给定的<strong>键小于根节点</strong>,就<strong>返回该键在左子树中的排名</strong>(递归计算).</li>
</ul>
<ul>
<li>如果给定的<strong>键大于根节点</strong>,就<strong>返回<code>t+1(根节点)</code>加上它在右子树中的排名</strong>(递归计算).</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">rank</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called rank() with a null key.&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> rank(root, key);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">rank</span><span class="params">(Node x, K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">    <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> rank(x.left, key);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span> + size(x.left) + rank(x.right, key);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">return</span> size(x.left);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="删除最大键和删除最小键"><a href="#删除最大键和删除最小键" class="headerlink" title="删除最大键和删除最小键"></a>删除最大键和删除最小键</h3><hr>
<p>对于删除最小键,需要不断深入根节点的左子树<strong>直至遇见一个空链接</strong>,然后将指向该节点的链接指向该节点的右子树(只需要在递归调用中返回它的右链接即可).此时已经没有任何链接指向要被删除的节点,因此它会被垃圾回收器gc掉.</p>
<p>删除最大键与其逻辑相似,只是方向相反.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Removes the smallest key and associated value from the symbol table.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NoSuchElementException if the symbol table is empty.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteMin</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (isEmpty())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;Symbol table underflow.&quot;</span>);</span><br><span class="line">    root = deleteMin(root);</span><br><span class="line">    <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">deleteMin</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// if the left link is empty,will link to the node of the right subtree.</span></span><br><span class="line">    <span class="keyword">if</span> (x.left == <span class="keyword">null</span>) <span class="keyword">return</span> x.right;</span><br><span class="line"></span><br><span class="line">    x.left = deleteMin(x.left);</span><br><span class="line">    x.size = size(x.left) + size(x.right) + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Removes the largest key and associated value from the symbol table.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NoSuchElementException if the symbol table is empty</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">deleteMax</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (isEmpty())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException(<span class="string">&quot;Symbol table underflow.&quot;</span>);</span><br><span class="line">    root = deleteMax(root);</span><br><span class="line">    <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">deleteMax</span><span class="params">(Node x)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// if the right link is empty,will link to the node of the left subtree.</span></span><br><span class="line">    <span class="keyword">if</span> (x.right == <span class="keyword">null</span>) <span class="keyword">return</span> x.left;</span><br><span class="line"></span><br><span class="line">    x.right = deleteMax(x.right);</span><br><span class="line">    x.size = size(x.left) + size(x.right) + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><hr>
<p>删除操作是二叉查找树中较为复杂的操作,假设我们要删除节点<code>x</code>(它是一个拥有两个子节点的节点),基本的实现逻辑如下.</p>
<ul>
<li><p>在删除节点<code>x</code>后用它的后继节点填补它的位置.</p>
</li>
<li><p>找出<code>x</code>的<strong>右子树中的最小节点</strong>(这样替换仍能保证树的有序性,因为<code>x.key</code>和它的后继节点的键之间不存在其他的键)做为<code>x</code>的后继节点.</p>
</li>
</ul>
<ul>
<li>将由此节点到根节点的路径上的所有节点的计数器减1(这里计数器的值仍然会被设为其所有子树中的节点总数加1).</li>
</ul>
<p><img src="http://algs4.cs.princeton.edu/32bst/images/bst-delete.png"></p>
<p>具体的过程如以下例子:</p>
<ol>
<li>将指向即将被删除的节点的链接保存为<code>t</code>.</li>
</ol>
<ol start="2">
<li>将<code>x</code>指向它的后继节点<code>min(t.right)</code>.</li>
</ol>
<ol start="3">
<li>将<code>x</code>的右链接(原本指向一棵所有节点都大于<code>x.key</code>的二叉查找树)指向<code>deleteMin(t.right)</code>,也就是在删除后所有的节点仍然都大于<code>x.key</code>的子二叉查找树.</li>
</ol>
<ol start="4">
<li>将<code>x</code>的左链接(本为空)指向<code>t.left</code>(其下所有的键都小于被删除的节点和它的后继节点).</li>
</ol>
<p>以上的实现逻辑有一个缺点,即是<strong>在某些实际应用场景下会产生性能问题</strong>,主要原因<strong>在于后继节点是一个随意的决定,且没有考虑树的对称性</strong>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">(K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called delete() with a null key.&quot;</span>);</span><br><span class="line">    root = delete(root, key);</span><br><span class="line">    <span class="function"><span class="keyword">assert</span> <span class="title">check</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Node <span class="title">delete</span><span class="params">(Node x, K key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> cmp = key.compareTo(x.key);</span><br><span class="line">    <span class="keyword">if</span> (cmp &lt; <span class="number">0</span>)</span><br><span class="line">        x.left = delete(x.left, key);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>)</span><br><span class="line">        x.right = delete(x.right, key);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (x.right == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> x.left;</span><br><span class="line">        <span class="keyword">if</span> (x.left == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> x.right;</span><br><span class="line">        Node t = x;</span><br><span class="line">        x = min(t.right);</span><br><span class="line">        x.right = deleteMin(t.right);</span><br><span class="line">        x.left = t.left;</span><br><span class="line">    &#125;</span><br><span class="line">    x.size = size(x.left) + size(x.right) + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="范围查找"><a href="#范围查找" class="headerlink" title="范围查找"></a>范围查找</h3><hr>
<p>实现一个范围查找的思路可以是:<strong>将所有落在给定范围以内的键放入一个队列<code>Queue</code>并跳过那些不可能含有所查找键的子树</strong>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Iterable&lt;K&gt; <span class="title">keys</span><span class="params">(K lo, K hi)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (lo == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called keys(lo,hi) first argument is null.&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (hi == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;called keys(lo,hi) second argument is null.&quot;</span>);</span><br><span class="line"></span><br><span class="line">    Queue&lt;K&gt; queue = <span class="keyword">new</span> Queue&lt;K&gt;();</span><br><span class="line">    keys(root, queue, lo, hi);</span><br><span class="line">    <span class="keyword">return</span> queue;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">keys</span><span class="params">(Node x, Queue&lt;K&gt; queue, K lo, K hi)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="keyword">null</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">int</span> cmp_lo = lo.compareTo(x.key);</span><br><span class="line">    <span class="keyword">int</span> cmp_hi = hi.compareTo(x.key);</span><br><span class="line">    <span class="keyword">if</span> (cmp_lo &lt; <span class="number">0</span>)</span><br><span class="line">        keys(x.left, queue, lo, hi);</span><br><span class="line">    <span class="keyword">if</span> (cmp_lo &lt;= <span class="number">0</span> &amp;&amp; cmp_hi &gt;= <span class="number">0</span>)</span><br><span class="line">        queue.enqueue(x.key);</span><br><span class="line">    <span class="keyword">if</span> (cmp_hi &gt; <span class="number">0</span>)</span><br><span class="line">        keys(x.right, queue, lo, hi);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><hr>
<p>二叉查找树的高度决定了它在最坏情况下的运行效率,但由于键的插入顺序不会是永远随机的,所以树的某一端高度可能会非常深,解决这个问题可以使用平衡二叉查找树,它能保证无论键的插入顺序如何,树的高度都将是总键数的对数.</p>
<p>本文中的实现皆采用递归的方式是为了提高可读性,二叉查找树可以使用非递归的方式实现且效率会更高.</p>
<p>二叉查找树结合了链表插入操作的灵活性和有序数组查找操作的高效性,且还有很多种优化的改进方案(例如平衡二叉查找树),总体来说二叉查找树是一种比较好的动态查找方法.</p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<ul>
<li>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></li>
</ul>
<ul>
<li>Email : <a href="mailto:&#x73;&#x79;&#108;&#x76;&#97;&#x6e;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#x78;&#116;&#x7a;&#64;&#49;&#54;&#51;&#x2e;&#x63;&#111;&#109;">&#x73;&#x79;&#108;&#x76;&#97;&#x6e;&#97;&#115;&#x73;&#117;&#x6e;&#x5f;&#x78;&#116;&#x7a;&#64;&#49;&#54;&#51;&#x2e;&#x63;&#111;&#109;</a></li>
</ul>
<ul>
<li>文中的完整实现代码见我的<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/algs4-study">GitHub</a> &amp; <a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/c4bddf50d94148470c0836af64449e8b">Gist</a></li>
</ul>
<ul>
<li>本文参考资料引用自<a target="_blank" rel="noopener" href="http://algs4.cs.princeton.edu/32bst/">&lt;&lt;Algorithms,4th Edition&gt;&gt;</a> &amp; <a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9">WikiPedia</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/03/20/2017-3-20-sorting_algorithm/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/03/20/2017-3-20-sorting_algorithm/" class="post-title-link" itemprop="url">谈谈几个常用的排序算法</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-03-20 18:00:00" itemprop="dateCreated datePublished" datetime="2017-03-20T18:00:00+08:00">2017-03-20</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">排序算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>最近在读&lt;&lt;Algorithms 4th&gt;&gt;时,了解到了很多常用的排序算法,故写一篇读书笔记记录下这些排序算法的思路和实现.</p>
</blockquote>
<h3 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h3><hr>
<p>冒泡排序是一种非常简单的初级排序算法,它每次比较相邻的两个元素,如果顺序错误就进行交换.由于最小的元素是经由不断交换慢慢浮到顶端的,所以叫做冒泡排序.</p>
<p>冒泡排序对n个元素需要<code>O(n^2)</code>次的比较次数,所以它对规模较大的数组进行排序是效率低下的.</p>
<h4 id="运行过程"><a href="#运行过程" class="headerlink" title="运行过程"></a>运行过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/3/37/Bubble_sort_animation.gif"></p>
<ol>
<li>比较相邻的两个元素,如果第二个元素小于第一个元素,则进行交换(降序则相反).</li>
</ol>
<ol start="2">
<li>对每一对相邻元素作同样的工作,从开始第一对直到最后一对.完成后,最后的元素将是最大的元素.</li>
</ol>
<ol start="3">
<li>针对所有的元素重复以上步骤,除了最后一个元素.</li>
</ol>
<ol start="4">
<li>持续地对每次越来越少的元素重复以上步骤,直到整个数组有序(即没有任何一对元素需要比较).</li>
</ol>
<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">   <span class="comment">// less与exch函数见完整代码</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; a.length - <span class="number">1</span> - i; j++) &#123;</span><br><span class="line">			<span class="keyword">if</span> (less(a[j + <span class="number">1</span>], a[j])) &#123;</span><br><span class="line">				exch(a, j, j + <span class="number">1</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Bubble Sort</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> SylvanasSun</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Bubble</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// This class should not be instantiated.</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="title">Bubble</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Rearranges the array in ascending order, using the natural order.</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@param</span> a</span></span><br><span class="line"><span class="comment">	 *            a the array to be sorted</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; a.length - <span class="number">1</span> - i; j++) &#123;</span><br><span class="line">				<span class="keyword">if</span> (less(a[j + <span class="number">1</span>], a[j])) &#123;</span><br><span class="line">					exch(a, j, j + <span class="number">1</span>);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * Rearranges the array in ascending order, using a comparator.</span></span><br><span class="line"><span class="comment">	 * </span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@param</span> a</span></span><br><span class="line"><span class="comment">	 *            a the arry to be sorted</span></span><br><span class="line"><span class="comment">	 * <span class="doctag">@param</span> comparator</span></span><br><span class="line"><span class="comment">	 *            comparator the comparator specifying the order</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Object[] a, Comparator comparator)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; a.length - <span class="number">1</span> - i; j++) &#123;</span><br><span class="line">				<span class="keyword">if</span> (less(comparator, a[j + <span class="number">1</span>], a[j])) &#123;</span><br><span class="line">					exch(a, j, j + <span class="number">1</span>);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// a &lt; b ?</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">less</span><span class="params">(Comparable a, Comparable b)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> a.compareTo(b) &lt; <span class="number">0</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// a &lt; b ?</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">less</span><span class="params">(Comparator comparator, Object a, Object b)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> comparator.compare(a, b) &lt; <span class="number">0</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// exchange a[i] and a[j]</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">exch</span><span class="params">(Object[] a, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">		Object temp = a[i];</span><br><span class="line">		a[i] = a[j];</span><br><span class="line">		a[j] = temp;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// print array elements to console</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length; i++) &#123;</span><br><span class="line">			System.out.print(a[i] + <span class="string">&quot; &quot;</span>);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// test</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">		String[] s = <span class="keyword">new</span> Scanner(System.in).nextLine().split(<span class="string">&quot;\\s+&quot;</span>);</span><br><span class="line">		Bubble.sort(s);</span><br><span class="line">		Bubble.print(s);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h3><hr>
<p>选择排序也是一种非常简单直观的初级排序算法,它的思想是不断地选择剩余元素之中的最小者.</p>
<p>它有以下两个特点.</p>
<ol>
<li><strong>运行时间与输入模型无关</strong> 在选择排序中,为了找出最小元素而扫描一遍数组并不能为下一轮扫描提供什么信息,即使输入是一个已经有序的数组或者是主键全部相等的数组和一个元素随机排列无序的数组所用的排序时间是一样长的.</li>
</ol>
<ol start="2">
<li><strong>数据移动是最少的</strong> 如果元素处于正确的位置上,则它不会被移动.选择排序每次交换一对元素，它们当中至少有一个将被移到其最终位置上，因此对n个元素的表进行排序总共进行至多n-1次交换.</li>
</ol>
<h4 id="运行过程-1"><a href="#运行过程-1" class="headerlink" title="运行过程"></a>运行过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/b/b0/Selection_sort_animation.gif"></p>
<ol>
<li>首先,找到数组中最小的那个元素</li>
</ol>
<ol start="2">
<li>其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素则它就和自己交换)</li>
</ol>
<ol start="3">
<li>再次,在剩下的元素中找到最小的元素,将它与数组第二个元素交换位置.如此往复,直到整个数组有序.</li>
</ol>
<h4 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length; i++) &#123;</span><br><span class="line">			<span class="keyword">int</span> min = i; <span class="comment">// the smallest element index</span></span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> j = i + <span class="number">1</span>; j &lt; a.length; j++) &#123;</span><br><span class="line">				<span class="keyword">if</span> (less(a[j], a[min]))</span><br><span class="line">					min = j;</span><br><span class="line">				exch(a, i, min);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<h3 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h3><hr>
<p>插入排序与选择排序一样,当前索引左边的所有元素都是有序的,但它们的最终位置并不是确定的.它构建了一个有序序列,对于未排序的元素,在有序序列中从后向前扫描,找到相应的位置并插入.</p>
<p>插入排序所需的时间取决于输入模型中元素的初始顺序.当输入模型是一个部分有序的数组时,插入排序的效率会高很多.</p>
<p>因此插入排序对于<strong>部分有序的数组</strong>十分高效,也很适合小规模的数组.</p>
<h4 id="运行过程-2"><a href="#运行过程-2" class="headerlink" title="运行过程"></a>运行过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif"></p>
<ol>
<li>从第一个元素开始,该元素可以认为已是有序的</li>
</ol>
<ol start="2">
<li>取出下一个元素,在有序序列中从后向前进行扫描</li>
</ol>
<ol start="3">
<li>如果该元素(已排序)大于新元素,则将该元素移到下一位置(右移)</li>
</ol>
<ol start="4">
<li>重复步骤3,直到找到已排序的元素小于或者等于新元素的位置</li>
</ol>
<ol start="5">
<li>将新元素插入到该位置后</li>
</ol>
<ol start="6">
<li>重复步骤2~5</li>
</ol>
<h4 id="代码实现-2"><a href="#代码实现-2" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; a.length; i++) &#123;</span><br><span class="line">			<span class="comment">// a[i] insert to a[i-1]、a[i-2]、a[i-3]...</span></span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> j = i; j &gt; <span class="number">0</span> &amp;&amp; less(a[j], a[j - <span class="number">1</span>]); j--) &#123;</span><br><span class="line">				exch(a, j, j - <span class="number">1</span>);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h4 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h4><p>插入排序还有很多可以优化的地方,这里例举两个案例.</p>
<ul>
<li><p>采用二分查找法来减少比较操作的次数.</p>
 <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">int</span> length = a.length;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; length; i++) &#123;</span><br><span class="line">		<span class="comment">// binary search to determine index j at which to insert a[i]</span></span><br><span class="line">		Comparable v = a[i];</span><br><span class="line">		<span class="keyword">int</span> lo = <span class="number">0</span>, hi = i;</span><br><span class="line">		<span class="keyword">while</span> (lo &lt; hi) &#123;</span><br><span class="line">			<span class="keyword">int</span> mid = lo + (hi - lo) / <span class="number">2</span>;</span><br><span class="line">			<span class="keyword">if</span> (less(v, a[mid]))</span><br><span class="line">				hi = mid;</span><br><span class="line">			<span class="keyword">else</span></span><br><span class="line">				lo = mid + <span class="number">1</span>;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// insertion sort with &quot;half exchanges&quot;</span></span><br><span class="line">		<span class="comment">// (insert a[i] at index j and shift a[j], ..., a[i-1] to right)</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> j = i; j &gt; lo; --j)</span><br><span class="line">			a[j] = a[j - <span class="number">1</span>];</span><br><span class="line">		a[lo] = v;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>在内循环中将较大的元素都向右移动而不总是交换两个元素(访问数组的次数能够减半)</p>
 <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">int</span> length = a.length;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// put smallest element in position to serve as sentinel</span></span><br><span class="line">	<span class="keyword">int</span> exchanges = <span class="number">0</span>;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = length - <span class="number">1</span>; i &gt; <span class="number">0</span>; i--) &#123;</span><br><span class="line">		<span class="keyword">if</span> (less(a[i], a[i - <span class="number">1</span>])) &#123;</span><br><span class="line">			exch(a, i, i - <span class="number">1</span>);</span><br><span class="line">			exchanges++;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (exchanges == <span class="number">0</span>)</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// insertion sort with half-exchanges</span></span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>; i &lt; length; i++) &#123;</span><br><span class="line">		Comparable v = a[i];</span><br><span class="line">		<span class="keyword">int</span> j = i;</span><br><span class="line">		<span class="keyword">while</span> (less(v, a[j - <span class="number">1</span>])) &#123;</span><br><span class="line">			a[j] = a[j - <span class="number">1</span>];</span><br><span class="line">			j--;</span><br><span class="line">		&#125;</span><br><span class="line">		a[j] = v;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h3></li>
</ul>
<hr>
<p>希尔排序,也称<strong>递减增量排序算法</strong>,它是基于插入排序的一种更高效的改进版本.</p>
<p>由于插入排序对于大规模乱序数组效率并不高,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端.</p>
<p>而希尔排序为了加快速度简单地改进了插入排序,<strong>交换不相邻的元素</strong>以对数组的局部进行排序,并最终用插入排序将<strong>局部有序</strong>的数组排序.</p>
<p>希尔排序的思想是<strong>使数组中任意间隔为h的元素都是有序的</strong>,可以说一个h有序的数组就是h个互相独立的有序数组编织在一起组成的一个数组.</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif" alt="以23, 10, 4, 1的步长序列进行希尔排序"></p>
<h4 id="代码实现-3"><a href="#代码实现-3" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> h = <span class="number">1</span>;</span><br><span class="line">		<span class="keyword">while</span> (h &lt; a.length / <span class="number">3</span>) &#123;</span><br><span class="line">			<span class="comment">// h sequence 1,4,13,40,121,364,1093,...</span></span><br><span class="line">			h = h * <span class="number">3</span> + <span class="number">1</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">while</span> (h &gt;= <span class="number">1</span>) &#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> i = h; i &lt; a.length; i++) &#123;</span><br><span class="line">				<span class="comment">// a[i] insert to a[i-h],a[i-2*h],a[i-3*h]...</span></span><br><span class="line">				<span class="keyword">for</span> (<span class="keyword">int</span> j = i; j &gt;= h &amp;&amp; less(a[j], a[j - h]); j -= h) &#123;</span><br><span class="line">					exch(a, j, j - h);</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">			h = h / <span class="number">3</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h3 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h3><hr>
<p>归并排序是分治算法的典型应用.所谓归并即是<strong>将两个有序的数组归并成一个更大的有序数组</strong>.</p>
<p>它有一个主要的缺点就是它需要额外的空间(辅助数组)并且所需的额外空间和N成正比.</p>
<h4 id="合并过程"><a href="#合并过程" class="headerlink" title="合并过程"></a>合并过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif"></p>
<ol>
<li>申请空间,使其大小为两个已有序序列之和,该空间用于存放合并后的序列</li>
</ol>
<ol start="2">
<li>声明两个指针,最初位置分别为两个有序序列的起始位置</li>
</ol>
<ol start="3">
<li>比较两个指针所指向的元素,选择相对小的元素放入合并空间中,并移动指针到下一个位置</li>
</ol>
<ol start="4">
<li>重复步骤3直到某一指针到达序列尾部</li>
</ol>
<ol start="5">
<li>将另一序列剩下的所有元素直接放入合并序列尾</li>
</ol>
<h4 id="自顶向下的归并排序"><a href="#自顶向下的归并排序" class="headerlink" title="自顶向下的归并排序"></a>自顶向下的归并排序</h4><p>自顶向下即是从顶部化整为零地递归解决问题.</p>
<p>例如:要对数组a[lo..hi]进行排序,需要先将它切分为a[lo..mid]与a[mid+1..hi]两部分,<strong>分别通过递归调用将它们单独排序</strong>,最后<strong>将有序的子数组归并为最终的排序结果</strong>.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// stably merge a[lo .. mid] with a[mid+1 ..hi] using aux[lo .. hi]</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge</span><span class="params">(Comparable[] a, Comparable[] aux, <span class="keyword">int</span> lo, <span class="keyword">int</span> mid, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">		<span class="comment">// copy a[] to aux[]</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> k = lo; k &lt;= hi; k++) &#123;</span><br><span class="line">			aux[k] = a[k];</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// merge back to a[]</span></span><br><span class="line">		<span class="keyword">int</span> i = lo, j = mid + <span class="number">1</span>;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> k = lo; k &lt;= hi; k++) &#123;</span><br><span class="line">			<span class="keyword">if</span> (i &gt; mid) &#123;</span><br><span class="line">				a[k] = aux[j++];</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (j &gt; hi) &#123;</span><br><span class="line">				a[k] = aux[i++];</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (less(aux[j], aux[i])) &#123;</span><br><span class="line">				a[k] = aux[j++];</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				a[k] = aux[i++];</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line"><span class="comment">// mergesort a[lo..hi] using auxiliary array aux[lo..hi]</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a, Comparable[] aux, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (hi &lt;= lo)</span><br><span class="line">			<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">int</span> mid = lo + (hi - lo) / <span class="number">2</span>;</span><br><span class="line">		sort(a, aux, lo, mid);</span><br><span class="line">		sort(a, aux, mid + <span class="number">1</span>, hi);</span><br><span class="line">		merge(a, aux, lo, mid, hi);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h4 id="自底向上的归并排序"><a href="#自底向上的归并排序" class="headerlink" title="自底向上的归并排序"></a>自底向上的归并排序</h4><p>自底向上则是循序渐进地解决问题.</p>
<p>实现思路是先归并那些微型数组,然后再成对归并得到的子数组,直到将整个数组归并在一起.</p>
<p>可以先进行两两归并(每个元素想象成一个大小为1的数组),然后进行四四归并(将两个大小为2的数组归并成一个有四个元素的数组),然后是八八归并…..(一直下去)在每一轮归并中,最后一次归并的第二个子数组可能比第一个子数组要小,如果不是的话所有归并中两个数组大小都应该一致.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//merge函数与自顶向下中的一致</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> N = a.length;</span><br><span class="line">		Comparable[] aux = <span class="keyword">new</span> Comparable[N];</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> len = <span class="number">1</span>; len &lt; N; len *= <span class="number">2</span>) &#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="keyword">int</span> lo = <span class="number">0</span>; lo &lt; N - len; lo += len + len) &#123;</span><br><span class="line">				<span class="keyword">int</span> mid = lo + len - <span class="number">1</span>;</span><br><span class="line">				<span class="keyword">int</span> hi = Math.min(lo + len + len - <span class="number">1</span>, N - <span class="number">1</span>);</span><br><span class="line">				merge(a, aux, lo, mid, hi);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h4 id="优化-1"><a href="#优化-1" class="headerlink" title="优化"></a>优化</h4><ul>
<li><p>如果数组很小,那么频繁的递归调用效率会很差,所以可以使用插入排序(或选择排序等)来处理小规模的子数组.</p>
 <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">merge</span><span class="params">(Comparable[] src, Comparable[] dst, <span class="keyword">int</span> lo, <span class="keyword">int</span> mid, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">int</span> i = lo, j = mid + <span class="number">1</span>;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> k = lo; k &lt;= hi; k++) &#123;</span><br><span class="line">		<span class="keyword">if</span> (i &gt; mid) &#123;</span><br><span class="line">			dst[k] = src[j++];</span><br><span class="line">		&#125; <span class="keyword">else</span> <span class="keyword">if</span> (j &gt; hi) &#123;</span><br><span class="line">			dst[k] = src[i++];</span><br><span class="line">		&#125; <span class="keyword">else</span> <span class="keyword">if</span> (less(src[j], src[i])) &#123;</span><br><span class="line">			dst[k] = src[j++];</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			dst[k] = src[i++];</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] src, Comparable[] dst, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">	<span class="comment">// if (hi &lt;= lo) return;</span></span><br><span class="line">	<span class="keyword">if</span> (hi &lt;= lo + CUTOFF) &#123;</span><br><span class="line">		insertionSort(dst, lo, hi);</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">int</span> mid = lo + (hi - lo) / <span class="number">2</span>;</span><br><span class="line">	sort(dst, src, lo, mid);</span><br><span class="line">	sort(dst, src, mid + <span class="number">1</span>, hi);</span><br><span class="line"></span><br><span class="line">	<span class="comment">// using System.arraycopy() is a bit faster than the above loop</span></span><br><span class="line">	<span class="keyword">if</span> (!less(src[mid + <span class="number">1</span>], src[mid])) &#123;</span><br><span class="line">		System.arraycopy(src, lo, dst, lo, hi - lo + <span class="number">1</span>);</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	merge(src, dst, lo, mid, hi);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// using insertion sort handle small array</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">insertionSort</span><span class="params">(Comparable[] a, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">int</span> i = lo; i &lt;= hi; i++) &#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> j = i; j &gt; lo &amp;&amp; less(a[j], a[j - <span class="number">1</span>]); j--) &#123;</span><br><span class="line">			exch(a, j, j - <span class="number">1</span>);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">	Comparable[] aux = a.clone();</span><br><span class="line">	sort(aux, a, <span class="number">0</span>, a.length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h3><hr>
<p>快速排序又称<code>划分交换排序</code>,它也是一种分治的排序算法.</p>
<p>快速排序有一个潜在的缺点,在<strong>切分不平衡时</strong>这个程序可能会极为低效,所以需要<strong>在快速排序前将数组随机排序</strong>来避免这种情况.</p>
<p>它将一个数组切分成两个子数组,将两部分独立地排序.它与归并排序不同的地方在于:</p>
<ul>
<li>归并排序将数组分成两个子数组分别排序,最终将有序的子数组归并以致整个数组排序.</li>
</ul>
<ul>
<li>快速排序将数组排序的方式则是当<strong>两个子数组都有序时,整个数组也就是有序的</strong>了.</li>
</ul>
<ul>
<li>在归并排序中,递归调用发生在处理整个数组<strong>之前</strong>;而在快速排序中,递归调用发生在处理整个数组<strong>之后</strong>.</li>
</ul>
<ul>
<li>在归并排序中,一个数组会被<strong>等分为两半</strong>,而在快速排序中,<strong>切分的位置取决于数组的内容</strong>.</li>
</ul>
<h4 id="运行过程-3"><a href="#运行过程-3" class="headerlink" title="运行过程"></a>运行过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif"></p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Partition_example.svg/302px-Partition_example.svg.png"></p>
<ol>
<li>先从数列中挑选出一个<code>基准</code>,可以为a[lo],它是被确认为排定的元素.</li>
</ol>
<ol start="2">
<li>从数组的左端(左指针)开始向右扫描直到找到一个大于等于<code>基准</code>的元素.</li>
</ol>
<ol start="3">
<li>从数组的右端(右指针)开始向左扫描直到找到一个小于等于<code>基准</code>的元素.</li>
</ol>
<ol start="4">
<li>这两个元素即是没有排定的,交换它们的位置(保证了左指针i的左侧元素都不大于<code>基准</code>,右指针j的右侧元素都不小于<code>基准</code>).</li>
</ol>
<ol start="5">
<li>.当两个指针相遇时,将<code>基准</code>和左子数组最右侧的元素(a[j])交换然后返回j即可.</li>
</ol>
<h4 id="代码实现-4"><a href="#代码实现-4" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// partition the subarray a[lo..hi] so that a[lo..j-1] &lt;= a[j] &lt;= a[j+1..hi]</span></span><br><span class="line">	<span class="comment">// and return the index j.</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">partition</span><span class="params">(Comparable[] a, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> i = lo; <span class="comment">// left point</span></span><br><span class="line">		<span class="keyword">int</span> j = hi + <span class="number">1</span>; <span class="comment">// right point</span></span><br><span class="line">		Comparable v = a[lo]; <span class="comment">// partition element</span></span><br><span class="line"></span><br><span class="line">		<span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">			<span class="comment">// scan left point</span></span><br><span class="line">			<span class="keyword">while</span> (less(a[++i], v)) &#123;</span><br><span class="line">				<span class="keyword">if</span> (i == hi)</span><br><span class="line">					<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// scan right point</span></span><br><span class="line">			<span class="keyword">while</span> (less(v, a[--j])) &#123;</span><br><span class="line">				<span class="keyword">if</span> (j == lo)</span><br><span class="line">					<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// check if point cross</span></span><br><span class="line">			<span class="keyword">if</span> (i &gt;= j)</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">			exch(a, i, j);</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// put partition element v to a[j]</span></span><br><span class="line">		exch(a, lo, j);</span><br><span class="line">		<span class="comment">// now a[lo..j-1] &lt;= a[j] &lt;= a[j+1..hi]</span></span><br><span class="line">		<span class="keyword">return</span> j;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (hi &lt;= lo)</span><br><span class="line">			<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">int</span> j = partition(a, lo, hi);</span><br><span class="line">		sort(a, lo, j - <span class="number">1</span>);</span><br><span class="line">		sort(a, j + <span class="number">1</span>, hi);</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		shuffle(a);</span><br><span class="line">		sort(a, <span class="number">0</span>, a.length - <span class="number">1</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// random sort an array</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">shuffle</span><span class="params">(Object[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (a == <span class="keyword">null</span>)</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;array is null.&quot;</span>);</span><br><span class="line">		Random random = <span class="keyword">new</span> Random();</span><br><span class="line">		<span class="keyword">int</span> N = a.length;</span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; N; i++) &#123;</span><br><span class="line">			<span class="keyword">int</span> j = i + random.nextInt(N - i);</span><br><span class="line">			Object temp = a[i];</span><br><span class="line">			a[i] = a[j];</span><br><span class="line">			a[j] = temp;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h4 id="三向切分的快速排序"><a href="#三向切分的快速排序" class="headerlink" title="三向切分的快速排序"></a>三向切分的快速排序</h4><p>当存在大量重复元素的情况下,快速排序的递归性会使元素全部重复的子数组经常出现,这就有很大的改进潜力,将当前快速排序从线性对数级别的性能提升至线性级别.</p>
<p>一个简单的思路是<strong>将数组切分为三部分</strong>,分别对应小于、等于、大于切分元素的数组元素.</p>
<p>在实现中,维护一个左指针<code>lt</code>使得<code>a[lo..lt-1]</code>的元素都小于<code>基准</code>,右指针<code>gt</code>使得<code>a[gt+1..hi]</code>中的元素都大于<code>基准</code>,一个指针<code>i</code>使得<code>a[lt..i-1]</code>中的元素都等于<code>基准</code>,<code>a[i..gt]</code>中的元素都还未确定.</p>
<ol>
<li><code>a[i]</code>小于<code>基准</code>,将<code>a[lt]</code>和<code>a[i]</code>交换,lt++&amp;i++.</li>
</ol>
<ol start="2">
<li><code>a[i]</code>大于<code>基准</code>,将<code>a[gt]</code>和<code>a[i]</code>交换,gt–.</li>
</ol>
<ol start="3">
<li><code>a[i]</code>等于<code>基准</code>,i++.</li>
</ol>
<p>以上操作都会保证数组元素不变且缩小<code>gt-i</code>的值(这样循环才会结束).除非和切分元素相等,其他元素都会被交换.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// quicksort the subarray a[lo .. hi] using 3-way partitioning</span></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a, <span class="keyword">int</span> lo, <span class="keyword">int</span> hi)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (hi &lt;= lo)</span><br><span class="line">			<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">int</span> lt = lo, i = lo + <span class="number">1</span>, gt = hi;</span><br><span class="line">		Comparable v = a[lo]; <span class="comment">// partition element</span></span><br><span class="line"></span><br><span class="line">		<span class="comment">// a[lo..lt-1] &lt; a[lt..gt] &lt; a[gt+1..hi]</span></span><br><span class="line">		<span class="keyword">while</span> (i &lt;= gt) &#123;</span><br><span class="line">			<span class="keyword">int</span> cmp = a[i].compareTo(v);</span><br><span class="line">			<span class="keyword">if</span> (cmp &lt; <span class="number">0</span>) &#123;</span><br><span class="line">				exch(a, i++, lt++);</span><br><span class="line">			&#125; <span class="keyword">else</span> <span class="keyword">if</span> (cmp &gt; <span class="number">0</span>) &#123;</span><br><span class="line">				exch(a, i, gt--);</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				i++;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		sort(a, lo, lt - <span class="number">1</span>);</span><br><span class="line">		sort(a, gt + <span class="number">1</span>, hi);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<h3 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h3><hr>
<p>堆排序是基于堆的优先队列实现的一种排序算法.</p>
<h4 id="优先队列"><a href="#优先队列" class="headerlink" title="优先队列"></a>优先队列</h4><p>优先队列是一种支持<strong>删除最大(最小)元素</strong>和<strong>插入元素</strong>的数据结构,它的内部是有序的,任意优先队列都可以变成一种排序方法.</p>
<h4 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h4><p>堆是一种数据结构,它通常可以被看作为一棵树的<strong>数组</strong>对象.将根节点作为最大数的叫做<strong>最大堆</strong>,反之,将根节点作为最小数的叫做<strong>最小堆</strong>.</p>
<p>堆是一个<strong>近似完全二叉树</strong>的结构,同时又满足了堆的性质:<strong>每个元素都要保证大于(小于)等于它的子节点的元素</strong>.</p>
<p>在一个堆中,根据根节点的索引位置不同,计算父节点与子节点位置的算法也不同.</p>
<ul>
<li>当数组起始位置为0时,位置k的节点的父节点为<code>(k - 1)/2</code>,它的两个子节点为<code>2k+1</code>,<code>2k+2</code>.</li>
</ul>
<ul>
<li>当数组起始位置为1时(即不使用索引0),位置k的节点的父节点为<code>k/2</code>,它的两个子节点为<code>2k</code>,<code>2k+1</code>.</li>
</ul>
<p>为了保证<strong>堆有序</strong>,需要支持两个操作用于<strong>打破堆的状态,然后再遍历堆并按照要求将堆的状态恢复</strong>,这个过程叫做<strong>堆的有序化</strong>.</p>
<ul>
<li><p><strong>由下至上的堆有序化(上浮)</strong> : 如果堆的有序状态因为某个节点变得比它的父节点更大而被打破时,那么就需要通过交换它和它的父节点来修复堆,将这个节点不断向上移动直到遇到了一个更大的父节点.(如果是最小堆,比较的逻辑相反).</p>
   <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在本文中,均不使用数组的0索引</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">swim</span><span class="params">(<span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">while</span> (k &gt; <span class="number">1</span> &amp;&amp; less(k/<span class="number">2</span>, k)) &#123;</span><br><span class="line">         exch(a,k, k/<span class="number">2</span>);</span><br><span class="line">         k = k/<span class="number">2</span>;</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>由上至下的堆有序化(下沉)</strong> : 如果堆的有序状态因为某个节点变得比它的两个子节点或是其中之一更小了而被打破时,需要通过将它和它的两个子节点中的较大者交换来修复堆,将这个节点向下移动直到它的子节点都比它更小或是到达了堆的底部.(如果是最小堆,比较的逻辑想法)</p>
 <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// n为数组长度</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sink</span><span class="params">(<span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">     <span class="keyword">while</span> (<span class="number">2</span>*k &lt;= n) &#123;</span><br><span class="line">         <span class="keyword">int</span> j = <span class="number">2</span>*k;</span><br><span class="line">         <span class="keyword">if</span> (j &lt; n &amp;&amp; less(j, j+<span class="number">1</span>)) j++;</span><br><span class="line">         <span class="keyword">if</span> (!less(a[k],a[j])) <span class="keyword">break</span>;</span><br><span class="line">         exch(a,k, j);</span><br><span class="line">         k = j;</span><br><span class="line">     &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h4 id="运行过程-4"><a href="#运行过程-4" class="headerlink" title="运行过程"></a>运行过程</h4><p><img src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif"></p>
<p>堆排序可以分为两个阶段.</p>
<ol>
<li><strong>堆的构造阶段,将原始数组重新组织安排进一个堆中</strong>.从右至左用sink()函数,构造子堆,数组的每个位置都已经是一个子堆的根节点.只需要扫描数组中的一半元素,因为我们可以跳过大小为1的子堆.最后在位置1上调用sink()函数,结束扫描.</li>
</ol>
<ol start="2">
<li><strong>下沉排序阶段,从堆中按递减顺序取出所有元素并得到排序结果</strong>.将堆中的最大元素删除,然后放入堆缩小后数组中空出的位置.</li>
</ol>
<h4 id="代码实现-5"><a href="#代码实现-5" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sort</span><span class="params">(Comparable[] a)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">int</span> N = a.length;</span><br><span class="line">		<span class="comment">// construction max heap</span></span><br><span class="line">		<span class="keyword">for</span> (<span class="keyword">int</span> k = N / <span class="number">2</span>; k &gt;= <span class="number">1</span>; k--) &#123;</span><br><span class="line">			sink(a, k, N);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">// sink sort</span></span><br><span class="line">		<span class="keyword">while</span> (N &gt; <span class="number">1</span>) &#123;</span><br><span class="line">			<span class="comment">// the biggest element (root) swap smallest element then heap shrink</span></span><br><span class="line">			exch(a, <span class="number">1</span>, N--);</span><br><span class="line">			<span class="comment">// new root element sink</span></span><br><span class="line">			sink(a, <span class="number">1</span>, N);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sink</span><span class="params">(Comparable[] pq, <span class="keyword">int</span> k, <span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">while</span> (<span class="number">2</span> * k &lt;= n) &#123;</span><br><span class="line">			<span class="keyword">int</span> j = <span class="number">2</span> * k;</span><br><span class="line">			<span class="keyword">if</span> (j &lt; n &amp;&amp; less(pq, j, j + <span class="number">1</span>))</span><br><span class="line">				j++;</span><br><span class="line">			<span class="keyword">if</span> (!less(pq, k, j))</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">			exch(pq, k, j);</span><br><span class="line">			k = j;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">less</span><span class="params">(Comparable[] pq, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> pq[i - <span class="number">1</span>].compareTo(pq[j - <span class="number">1</span>]) &lt; <span class="number">0</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">exch</span><span class="params">(Object[] pq, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">		Object swap = pq[i - <span class="number">1</span>];</span><br><span class="line">		pq[i - <span class="number">1</span>] = pq[j - <span class="number">1</span>];</span><br><span class="line">		pq[j - <span class="number">1</span>] = swap;</span><br><span class="line">	&#125;	</span><br></pre></td></tr></table></figure>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><hr>
<table>
<thead>
<tr>
<th>名称</th>
<th>是否稳定</th>
<th>是否为原地排序</th>
<th>时间复杂度</th>
<th>空间复杂度</th>
<th>备注</th>
</tr>
</thead>
<tbody><tr>
<td>冒泡排序</td>
<td>是</td>
<td>是</td>
<td>O(N^2)</td>
<td>O(1)</td>
<td>（无序区，有序区）。从无序区通过交换找出最大元素放到有序区前端。</td>
</tr>
<tr>
<td>选择排序</td>
<td>否</td>
<td>是</td>
<td>O(N^2)</td>
<td>O(1)</td>
<td>（有序区，无序区）。在无序区里找一个最小的元素跟在有序区的后面。对数组：比较得多，换得少。</td>
</tr>
<tr>
<td>插入排序</td>
<td>是</td>
<td>是</td>
<td>介入N和N^2之间</td>
<td>O(1)</td>
<td>（有序区，无序区）。把无序区的第一个元素插入到有序区的合适的位置。对数组：比较得少，换得多。</td>
</tr>
<tr>
<td>希尔排序</td>
<td>否</td>
<td>是</td>
<td>O(N log^2 N)</td>
<td>O(1)</td>
<td>每一轮按照事先决定的间隔进行插入排序，间隔会依次缩小，最后一次一定要是1。</td>
</tr>
<tr>
<td>快速排序</td>
<td>否</td>
<td>是</td>
<td>O(N log N)</td>
<td>O(logN)</td>
<td>（小数，基准元素，大数）。在区间中随机挑选一个元素作基准，将小于基准的元素放在基准之前，大于基准的元素放在基准之后，再分别对小数区与大数区进行排序。</td>
</tr>
<tr>
<td>三向快速排序</td>
<td>否</td>
<td>是</td>
<td>介于N和NlogN之间</td>
<td>O(logN)</td>
<td>对含有大量重复元素的输入数据效率较高。</td>
</tr>
<tr>
<td>归并排序</td>
<td>是</td>
<td>否</td>
<td>O(N log N)</td>
<td>O(N)</td>
<td>把数据分为两段，从两段中逐个选最小的元素移入新数据段的末尾。</td>
</tr>
<tr>
<td>堆排序</td>
<td>否</td>
<td>是</td>
<td>O(N log N)</td>
<td>O(1)</td>
<td>（最大堆，有序区）。从堆顶把根卸出来放在有序区之前，再恢复堆。</td>
</tr>
</tbody></table>
<p>在大多数实际情况中,快速排序是最佳选择.如果稳定性很重要而空间又不是问题的情况下,归并排序可能是最好的.但是在运行时间至关重要的任何排序应用中应该认真地考虑使用快速排序.</p>
<p>在JDK中,Arrays.sort()选择了根据不同的参数类型,来使用不同的排序算法.如果是原始数据类型则使用三向切分的快速排序,对引用类型则使用归并排序.</p>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<ul>
<li>Author : <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun</a></li>
</ul>
<ul>
<li>Email : <a href="mailto:&#x73;&#121;&#x6c;&#x76;&#97;&#110;&#x61;&#x73;&#x73;&#117;&#x6e;&#95;&#120;&#x74;&#x7a;&#x40;&#x31;&#x36;&#x33;&#x2e;&#x63;&#x6f;&#109;">&#x73;&#121;&#x6c;&#x76;&#97;&#110;&#x61;&#x73;&#x73;&#117;&#x6e;&#95;&#120;&#x74;&#x7a;&#x40;&#x31;&#x36;&#x33;&#x2e;&#x63;&#x6f;&#109;</a></li>
</ul>
<ul>
<li>文中的完整实现代码见我的<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun/algs4-study">GitHub</a> &amp; <a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/6706d008e735778c49659883f23f1eff">Gist</a></li>
</ul>
<ul>
<li>本文参考资料引用自<a target="_blank" rel="noopener" href="http://algs4.cs.princeton.edu/20sorting/">&lt;&lt;Algorithms,4th&gt;&gt;</a> &amp; <a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95">WikiPedia</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/03/06/2017-3-06-LinkedStack&Queue/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/yuwanzi.io/2017/03/06/2017-3-06-LinkedStack&Queue/" class="post-title-link" itemprop="url">使用链表做为Stack、Queue中的数据表示结构的基本思路</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-03-06 18:00:00" itemprop="dateCreated datePublished" datetime="2017-03-06T18:00:00+08:00">2017-03-06</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/" itemprop="url" rel="index"><span itemprop="name">Algorithms</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/Algorithms/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/LinkedTable/" itemprop="url" rel="index"><span itemprop="name">LinkedTable</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="什么是链表"><a href="#什么是链表" class="headerlink" title="什么是链表?"></a>什么是链表?</h3><hr>
<p>链表是一种常见的基础数据结构(数组也是基础数据结构),它是一种递归的数据结构,由一系列节点(Node)组成,节点含有一个存储数据的数据域和一个指向下一个节点地址位置的引用.</p>
<p>链表是线性表的一种,但是它的物理存储结构是非连续、 非顺序的,元素的逻辑顺序是通过节点之间的链接确定的.</p>
<h3 id="数据结构与数据类型的区别"><a href="#数据结构与数据类型的区别" class="headerlink" title="数据结构与数据类型的区别"></a>数据结构与数据类型的区别</h3><hr>
<ul>
<li><p><font color="red">数据类型</font>是一组数据和一组对这些值进行操作的集合.</p>
</li>
<li><p><font color="red">数据结构</font>强调的是数据的存储和组织方式.</p>
</li>
</ul>
<p>常见的数据结构有:数组、栈、队列、链表、树、图、堆、散列表.</p>
<h3 id="链表是否可以替代数组"><a href="#链表是否可以替代数组" class="headerlink" title="链表是否可以替代数组?"></a>链表是否可以替代数组?</h3><hr>
<p>由于创建数组需要预先知道数组的大小,所以想要动态的扩容需要不断地创建新数组,而链表则可以充分利用内存空间,实现较为灵活的动态扩展.</p>
<p>使用链表替代数组有优点也有缺点:</p>
<p><strong>优点</strong></p>
<ol>
<li>在链表中进行插入操作或是删除操作都更加方便快速.</li>
</ol>
<ol start="2">
<li>链表所需的空间总是和集合的大小成正比.</li>
</ol>
<ol start="3">
<li>链表操作所需的时间总是和集合的大小无关.</li>
</ol>
<p><strong>缺点</strong></p>
<ol>
<li>无法像数组一样可以通过索引来进行随机访问.</li>
</ol>
<ol start="2">
<li>由于每一个元素节点都是一个对象,所以需要的空间开销比较大.</li>
</ol>
<h3 id="Stack"><a href="#Stack" class="headerlink" title="Stack"></a>Stack</h3><hr>
<p><img src="/img/note-img/2017-3-06-LinkedStack&Queue/LinkedStack.png"></p>
<p>栈是一种基于后进先出(LIFO)的数据结构,其中的元素除了头尾之外,每个元素都有一个前驱和一个后继.</p>
<p>使用链表来表示栈内部的数据时,栈顶就是链表的头部,当push元素时将元素添加在表头,当pop元素时将元素从表头删除.</p>
<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用链表实现的可迭代的下压栈(后进先出)</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * Created by SylvanasSun on 2017/3/6.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Stack</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Iterable</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node first; <span class="comment">//栈顶(链表头部)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> N; <span class="comment">//元素个数</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用于表示链表中的节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        T item;</span><br><span class="line">        Node next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断Stack是否为空</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true代表Stack为空, false为未空</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> first == <span class="keyword">null</span>; <span class="comment">//也可以用N==0来判断</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回Stack中的元素数量</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 元素数量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> N;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将元素t添加到栈顶</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> t 添加的元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">push</span><span class="params">(T t)</span> </span>&#123;</span><br><span class="line">        Node oldFirst = first;</span><br><span class="line">        first = <span class="keyword">new</span> Node();</span><br><span class="line">        first.item = t;</span><br><span class="line">        first.next = oldFirst;</span><br><span class="line">        N++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将栈顶的元素弹出</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 栈顶节点的item</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        T item = first.item;</span><br><span class="line">        first = first.next;</span><br><span class="line">        N--;</span><br><span class="line">        <span class="keyword">return</span> item;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Iterator&lt;T&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ListIterator();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 迭代器,维护了一个实例变量current来记录链表的当前结点.</span></span><br><span class="line"><span class="comment">     * 这段代码可以在Stack和Queue之间复用,因为它们内部数据的数据结构是相同的,</span></span><br><span class="line"><span class="comment">     * 只是访问顺序分别为后进先出和先进先出而已.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ListIterator</span> <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> Node current = first;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> current != <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            T item = current.item;</span><br><span class="line">            current = current.next;</span><br><span class="line">            <span class="keyword">return</span> item;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="Queue"><a href="#Queue" class="headerlink" title="Queue"></a>Queue</h3><hr>
<p><img src="/img/note-img/2017-3-06-LinkedStack&Queue/LinkedQueue.png"></p>
<p>队列是一种基于先进先出(FIFO)的数据结构,元素的处理顺序就是它们被添加到队列中的顺序.</p>
<p>可以使用实例变量first指向队列的队头,实例变量last指向队列的队尾,当将一个元素入列时,就将这个元素添加到队尾,当要将一个元素出列时,就删除队头的节点.</p>
<h4 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用链表实现的Queue,它与Stack的区别在于链表的访问顺序.</span></span><br><span class="line"><span class="comment"> * Queue的访问顺序是先进先出的.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * Created by SylvanasSun on 2017/3/6.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Queue</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Iterable</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Node first; <span class="comment">//链表头部,即队头</span></span><br><span class="line">    <span class="keyword">private</span> Node last; <span class="comment">//链表尾部,即队尾</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> N; <span class="comment">//size</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用于表示链表中的节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        T item;</span><br><span class="line">        Node next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断Queue是否为空</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true代表Stack为空, false为未空</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> first == <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回Queue中的元素数量</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 元素数量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> N;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 入队,向队尾添加新的元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 添加的元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">enqueue</span><span class="params">(T item)</span> </span>&#123;</span><br><span class="line">        Node oldLast = last;</span><br><span class="line">        last = <span class="keyword">new</span> Node();</span><br><span class="line">        last.item = item;</span><br><span class="line">        last.next = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 如果队列为空,队头指向队尾(队列中只有一个元素),</span></span><br><span class="line"><span class="comment">         * 否则将旧的队尾的next指向新的队尾</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) first = last;</span><br><span class="line">        <span class="keyword">else</span> oldLast.next = last;</span><br><span class="line">        N++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 出队,将队头节点弹出队列</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 队头节点的item</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">dequeue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        T item = first.item;</span><br><span class="line">        first = first.next;</span><br><span class="line">        N--;</span><br><span class="line">        <span class="comment">//如果队列为空,队尾则为null</span></span><br><span class="line">        <span class="keyword">if</span> (isEmpty()) last = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">return</span> item;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Iterator&lt;T&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ListIterator();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 迭代器,与Stack中的实现一致</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ListIterator</span> <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> Node current = first;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> current != <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            T item = current.item;</span><br><span class="line">            current = current.next;</span><br><span class="line">            <span class="keyword">return</span> item;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="end"><a href="#end" class="headerlink" title="end"></a>end</h3><hr>
<ul>
<li>Author: SylvanasSun</li>
<li>GitHub: <a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">https://github.com/SylvanasSun</a></li>
<li>Email: <a href="mailto:&#x73;&#x79;&#108;&#x76;&#x61;&#x6e;&#x61;&#115;&#115;&#x75;&#110;&#x5f;&#x78;&#x74;&#122;&#x40;&#x31;&#54;&#51;&#46;&#99;&#x6f;&#x6d;">&#x73;&#x79;&#108;&#x76;&#x61;&#x6e;&#x61;&#115;&#115;&#x75;&#110;&#x5f;&#x78;&#x74;&#122;&#x40;&#x31;&#54;&#51;&#46;&#99;&#x6f;&#x6d;</a></li>
<li>Reference: 《Algorithms 4th edition》&amp; <a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8">wiki</a></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/yuwanzi.io/page/3/"><i class="fa fa-angle-left" aria-label="Vorherige Seite"></i></a><a class="page-number" href="/yuwanzi.io/">1</a><span class="space">&hellip;</span><a class="page-number" href="/yuwanzi.io/page/3/">3</a><span class="page-number current">4</span><a class="page-number" href="/yuwanzi.io/page/5/">5</a><span class="space">&hellip;</span><a class="page-number" href="/yuwanzi.io/page/7/">7</a><a class="extend next" rel="next" href="/yuwanzi.io/page/5/"><i class="fa fa-angle-right" aria-label="Nächste Seite"></i></a>
  </nav>


<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">玉丸子</span>
</div>
  <div class="powered-by">Erstellt mit  <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/yuwanzi.io/js/utils.js"></script><script src="/yuwanzi.io/js/motion.js"></script><script src="/yuwanzi.io/js/schemes/muse.js"></script><script src="/yuwanzi.io/js/next-boot.js"></script>

  






  





</body>
</html>
